Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1790)

Side by Side Diff: sky/sdk/lib/framework/layout2.dart

Issue 1161003002: Split layout2.dart into several files (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/sdk/lib/framework/fn2.dart ('k') | sky/sdk/lib/framework/rendering/render_block.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 'node.dart';
6 import 'dart:sky' as sky;
7
8 // ABSTRACT LAYOUT
9
10 class ParentData {
11 void detach() {
12 detachSiblings();
13 }
14 void detachSiblings() { } // workaround for lack of inter-class mixins in Dart
15 void merge(ParentData other) {
16 // override this in subclasses to merge in data from other into this
17 assert(other.runtimeType == this.runtimeType);
18 }
19 }
20
21 const kLayoutDirections = 4;
22
23 double clamp({double min: 0.0, double value: 0.0, double max: double.INFINITY}) {
24 assert(min != null);
25 assert(value != null);
26 assert(max != null);
27
28 if (value > max)
29 value = max;
30 if (value < min)
31 value = min;
32 return value;
33 }
34
35 class RenderNodeDisplayList extends sky.PictureRecorder {
36 RenderNodeDisplayList(double width, double height) : super(width, height);
37 void paintChild(RenderNode child, sky.Point position) {
38 save();
39 translate(position.x, position.y);
40 child.paint(this);
41 restore();
42 }
43 }
44
45 abstract class RenderNode extends AbstractNode {
46
47 // LAYOUT
48
49 // parentData is only for use by the RenderNode that actually lays this
50 // node out, and any other nodes who happen to know exactly what
51 // kind of node that is.
52 ParentData parentData;
53 void setParentData(RenderNode child) {
54 // override this to setup .parentData correctly for your class
55 if (child.parentData is! ParentData)
56 child.parentData = new ParentData();
57 }
58
59 void adoptChild(RenderNode child) { // only for use by subclasses
60 // call this whenever you decide a node is a child
61 assert(child != null);
62 setParentData(child);
63 super.adoptChild(child);
64 }
65 void dropChild(RenderNode child) { // only for use by subclasses
66 assert(child != null);
67 assert(child.parentData != null);
68 child.parentData.detach();
69 super.dropChild(child);
70 }
71
72 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>();
73 static bool _debugDoingLayout = false;
74 bool _needsLayout = true;
75 bool get needsLayout => _needsLayout;
76 RenderNode _relayoutSubtreeRoot;
77 dynamic _constraints;
78 dynamic get constraints => _constraints;
79 bool debugAncestorsAlreadyMarkedNeedsLayout() {
80 if (_relayoutSubtreeRoot == null)
81 return true; // we haven't yet done layout even once, so there's nothing f or us to do
82 RenderNode node = this;
83 while (node != _relayoutSubtreeRoot) {
84 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot);
85 assert(node.parent != null);
86 node = node.parent as RenderNode;
87 if (!node._needsLayout)
88 return false;
89 }
90 assert(node._relayoutSubtreeRoot == node);
91 return true;
92 }
93 void markNeedsLayout() {
94 assert(!_debugDoingLayout);
95 assert(!_debugDoingPaint);
96 if (_needsLayout) {
97 assert(debugAncestorsAlreadyMarkedNeedsLayout());
98 return;
99 }
100 _needsLayout = true;
101 assert(_relayoutSubtreeRoot != null);
102 if (_relayoutSubtreeRoot != this) {
103 assert(parent is RenderNode);
104 parent.markNeedsLayout();
105 } else {
106 _nodesNeedingLayout.add(this);
107 }
108 }
109 static void flushLayout() {
110 _debugDoingLayout = true;
111 List<RenderNode> dirtyNodes = _nodesNeedingLayout;
112 _nodesNeedingLayout = new List<RenderNode>();
113 dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) {
114 if (node._needsLayout && node.attached)
115 node._doLayout();
116 });
117 _debugDoingLayout = false;
118 }
119 void _doLayout() {
120 try {
121 assert(_relayoutSubtreeRoot == this);
122 performLayout();
123 } catch (e, stack) {
124 print('Exception raised during layout of ${this}: ${e}');
125 print(stack);
126 return;
127 }
128 _needsLayout = false;
129 }
130 void layout(dynamic constraints, { bool parentUsesSize: false }) {
131 RenderNode relayoutSubtreeRoot;
132 if (!parentUsesSize || sizedByParent || parent is! RenderNode)
133 relayoutSubtreeRoot = this;
134 else
135 relayoutSubtreeRoot = parent._relayoutSubtreeRoot;
136 if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _r elayoutSubtreeRoot)
137 return;
138 _constraints = constraints;
139 _relayoutSubtreeRoot = relayoutSubtreeRoot;
140 if (sizedByParent)
141 performResize();
142 performLayout();
143 _needsLayout = false;
144 markNeedsPaint();
145 }
146 bool get sizedByParent => false; // return true if the constraints are the onl y input to the sizing algorithm (in particular, child nodes have no impact)
147 void performResize(); // set the local dimensions, using only the constraints (only called if sizedByParent is true)
148 void performLayout();
149 // Override this to perform relayout without your parent's
150 // involvement.
151 //
152 // This is called during layout. If sizedByParent is true, then
153 // performLayout() should not change your dimensions, only do that
154 // in performResize(). If sizedByParent is false, then set both
155 // your dimensions and do your children's layout here.
156 //
157 // When calling layout() on your children, pass in
158 // "parentUsesSize: true" if your size or layout is dependent on
159 // your child's size.
160
161 // when the parent has rotated (e.g. when the screen has been turned
162 // 90 degrees), immediately prior to layout() being called for the
163 // new dimensions, rotate() is called with the old and new angles.
164 // The next time paint() is called, the coordinate space will have
165 // been rotated N quarter-turns clockwise, where:
166 // N = newAngle-oldAngle
167 // ...but the rendering is expected to remain the same, pixel for
168 // pixel, on the output device. Then, the layout() method or
169 // equivalent will be invoked.
170
171 void rotate({
172 int oldAngle, // 0..3
173 int newAngle, // 0..3
174 Duration time
175 }) { }
176
177
178 // PAINTING
179
180 static bool _debugDoingPaint = false;
181 void markNeedsPaint() {
182 assert(!_debugDoingPaint);
183 // TODO(abarth): It's very redundant to call this for every node in the
184 // render tree during layout. We should instead compute a summary bit and
185 // call it once at the end of layout.
186 sky.view.scheduleFrame();
187 }
188 void paint(RenderNodeDisplayList canvas) { }
189
190
191 // HIT TESTING
192
193 void handlePointer(sky.PointerEvent event) {
194 // override this if you have a client, to hand it to the client
195 // override this if you want to do anything with the pointer event
196 }
197
198 // RenderNode subclasses are expected to have a method like the
199 // following (with the signature being whatever passes for coordinates
200 // for this particular class):
201 // bool hitTest(HitTestResult result, { sky.Point position }) {
202 // // If (x,y) is not inside this node, then return false. (You
203 // // can assume that the given coordinate is inside your
204 // // dimensions. You only need to check this if you're an
205 // // irregular shape, e.g. if you have a hole.)
206 // // Otherwise:
207 // // For each child that intersects x,y, in z-order starting from the top,
208 // // call hitTest() for that child, passing it /result/, and the coordinate s
209 // // converted to the child's coordinate origin, and stop at the first chil d
210 // // that returns true.
211 // // Then, add yourself to /result/, and return true.
212 // }
213 // You must not add yourself to /result/ if you return false.
214
215 }
216
217 class HitTestResult {
218 final List<RenderNode> path = new List<RenderNode>();
219
220 RenderNode get result => path.first;
221
222 void add(RenderNode node) {
223 path.add(node);
224 }
225 }
226
227
228 // GENERIC MIXIN FOR RENDER NODES WITH ONE CHILD
229
230 abstract class RenderNodeWithChildMixin<ChildType extends RenderNode> {
231 ChildType _child;
232 ChildType get child => _child;
233 void set child (ChildType value) {
234 if (_child != null)
235 dropChild(_child);
236 _child = value;
237 if (_child != null)
238 adoptChild(_child);
239 markNeedsLayout();
240 }
241 }
242
243
244 // GENERIC MIXIN FOR RENDER NODES WITH A LIST OF CHILDREN
245
246 abstract class ContainerParentDataMixin<ChildType extends RenderNode> {
247 ChildType previousSibling;
248 ChildType nextSibling;
249 void detachSiblings() {
250 if (previousSibling != null) {
251 assert(previousSibling.parentData is ContainerParentDataMixin<ChildType>);
252 assert(previousSibling != this);
253 assert(previousSibling.parentData.nextSibling == this);
254 previousSibling.parentData.nextSibling = nextSibling;
255 }
256 if (nextSibling != null) {
257 assert(nextSibling.parentData is ContainerParentDataMixin<ChildType>);
258 assert(nextSibling != this);
259 assert(nextSibling.parentData.previousSibling == this);
260 nextSibling.parentData.previousSibling = previousSibling;
261 }
262 previousSibling = null;
263 nextSibling = null;
264 }
265 }
266
267 abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentData Type extends ContainerParentDataMixin<ChildType>> implements RenderNode {
268 // abstract class that has only InlineNode children
269
270 bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
271 assert(child.parentData is ParentDataType);
272 while (child.parentData.previousSibling != null) {
273 assert(child.parentData.previousSibling != child);
274 child = child.parentData.previousSibling;
275 assert(child.parentData is ParentDataType);
276 }
277 return child == equals;
278 }
279 bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
280 assert(child.parentData is ParentDataType);
281 while (child.parentData.nextSibling != null) {
282 assert(child.parentData.nextSibling != child);
283 child = child.parentData.nextSibling;
284 assert(child.parentData is ParentDataType);
285 }
286 return child == equals;
287 }
288
289 ChildType _firstChild;
290 ChildType _lastChild;
291 void add(ChildType child, { ChildType before }) {
292 assert(child != this);
293 assert(before != this);
294 assert(child != before);
295 assert(child != _firstChild);
296 assert(child != _lastChild);
297 adoptChild(child);
298 assert(child.parentData is ParentDataType);
299 assert(child.parentData.nextSibling == null);
300 assert(child.parentData.previousSibling == null);
301 if (before == null) {
302 // append at the end (_lastChild)
303 child.parentData.previousSibling = _lastChild;
304 if (_lastChild != null) {
305 assert(_lastChild.parentData is ParentDataType);
306 _lastChild.parentData.nextSibling = child;
307 }
308 _lastChild = child;
309 if (_firstChild == null)
310 _firstChild = child;
311 } else {
312 assert(_firstChild != null);
313 assert(_lastChild != null);
314 assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
315 assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
316 assert(before.parentData is ParentDataType);
317 if (before.parentData.previousSibling == null) {
318 // insert at the start (_firstChild); we'll end up with two or more chil dren
319 assert(before == _firstChild);
320 child.parentData.nextSibling = before;
321 before.parentData.previousSibling = child;
322 _firstChild = child;
323 } else {
324 // insert in the middle; we'll end up with three or more children
325 // set up links from child to siblings
326 child.parentData.previousSibling = before.parentData.previousSibling;
327 child.parentData.nextSibling = before;
328 // set up links from siblings to child
329 assert(child.parentData.previousSibling.parentData is ParentDataType);
330 assert(child.parentData.nextSibling.parentData is ParentDataType);
331 child.parentData.previousSibling.parentData.nextSibling = child;
332 child.parentData.nextSibling.parentData.previousSibling = child;
333 assert(before.parentData.previousSibling == child);
334 }
335 }
336 markNeedsLayout();
337 }
338 void remove(ChildType child) {
339 assert(child.parentData is ParentDataType);
340 assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
341 assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
342 if (child.parentData.previousSibling == null) {
343 assert(_firstChild == child);
344 _firstChild = child.parentData.nextSibling;
345 } else {
346 assert(child.parentData.previousSibling.parentData is ParentDataType);
347 child.parentData.previousSibling.parentData.nextSibling = child.parentData .nextSibling;
348 }
349 if (child.parentData.nextSibling == null) {
350 assert(_lastChild == child);
351 _lastChild = child.parentData.previousSibling;
352 } else {
353 assert(child.parentData.nextSibling.parentData is ParentDataType);
354 child.parentData.nextSibling.parentData.previousSibling = child.parentData .previousSibling;
355 }
356 child.parentData.previousSibling = null;
357 child.parentData.nextSibling = null;
358 dropChild(child);
359 markNeedsLayout();
360 }
361 void redepthChildren() {
362 ChildType child = _firstChild;
363 while (child != null) {
364 redepthChild(child);
365 assert(child.parentData is ParentDataType);
366 child = child.parentData.nextSibling;
367 }
368 }
369 void attachChildren() {
370 ChildType child = _firstChild;
371 while (child != null) {
372 child.attach();
373 assert(child.parentData is ParentDataType);
374 child = child.parentData.nextSibling;
375 }
376 }
377 void detachChildren() {
378 ChildType child = _firstChild;
379 while (child != null) {
380 child.detach();
381 assert(child.parentData is ParentDataType);
382 child = child.parentData.nextSibling;
383 }
384 }
385
386 ChildType get firstChild => _firstChild;
387 ChildType get lastChild => _lastChild;
388 ChildType childAfter(ChildType child) {
389 assert(child.parentData is ParentDataType);
390 return child.parentData.nextSibling;
391 }
392
393 }
394
395
396 // GENERIC BOX RENDERING
397 // Anything that has a concept of x, y, width, height is going to derive from th is
398
399 class EdgeDims {
400 // used for e.g. padding
401 const EdgeDims(this.top, this.right, this.bottom, this.left);
402 final double top;
403 final double right;
404 final double bottom;
405 final double left;
406 operator ==(EdgeDims other) => (top == other.top) ||
407 (right == other.right) ||
408 (bottom == other.bottom) ||
409 (left == other.left);
410 }
411
412 class BoxConstraints {
413 const BoxConstraints({
414 this.minWidth: 0.0,
415 this.maxWidth: double.INFINITY,
416 this.minHeight: 0.0,
417 this.maxHeight: double.INFINITY});
418
419 BoxConstraints.tight(sky.Size size)
420 : minWidth = size.width,
421 maxWidth = size.width,
422 minHeight = size.height,
423 maxHeight = size.height;
424
425 BoxConstraints deflate(EdgeDims edges) {
426 assert(edges != null);
427 return new BoxConstraints(
428 minWidth: minWidth,
429 maxWidth: maxWidth - (edges.left + edges.right),
430 minHeight: minHeight,
431 maxHeight: maxHeight - (edges.top + edges.bottom)
432 );
433 }
434
435 final double minWidth;
436 final double maxWidth;
437 final double minHeight;
438 final double maxHeight;
439
440 double constrainWidth(double width) {
441 return clamp(min: minWidth, max: maxWidth, value: width);
442 }
443
444 double constrainHeight(double height) {
445 return clamp(min: minHeight, max: maxHeight, value: height);
446 }
447
448 sky.Size constrain(sky.Size size) {
449 return new sky.Size(constrainWidth(size.width), constrainHeight(size.height) );
450 }
451
452 bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFI NITY;
453 }
454
455 class BoxParentData extends ParentData {
456 sky.Point position = new sky.Point(0.0, 0.0);
457 }
458
459 abstract class RenderBox extends RenderNode {
460
461 void setParentData(RenderNode child) {
462 if (child.parentData is! BoxParentData)
463 child.parentData = new BoxParentData();
464 }
465
466 // override this to report what dimensions you would have if you
467 // were laid out with the given constraints this can walk the tree
468 // if it must, but it should be as cheap as possible; just get the
469 // dimensions and nothing else (e.g. don't calculate hypothetical
470 // child positions if they're not needed to determine dimensions)
471 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
472 return constraints.constrain(new sky.Size(0.0, 0.0));
473 }
474
475 BoxConstraints get constraints => super.constraints as BoxConstraints;
476 void performResize() {
477 // default behaviour for subclasses that have sizedByParent = true
478 size = constraints.constrain(new sky.Size(0.0, 0.0));
479 assert(size.height < double.INFINITY);
480 assert(size.width < double.INFINITY);
481 }
482 void performLayout() {
483 // descendants have to either override performLayout() to set both
484 // width and height and lay out children, or, set sizedByParent to
485 // true so that performResize()'s logic above does its thing.
486 assert(sizedByParent);
487 }
488
489 bool hitTest(HitTestResult result, { sky.Point position }) {
490 hitTestChildren(result, position: position);
491 result.add(this);
492 return true;
493 }
494 void hitTestChildren(HitTestResult result, { sky.Point position }) { }
495
496 sky.Size size = new sky.Size(0.0, 0.0);
497 }
498
499 abstract class RenderProxyBox extends RenderBox with RenderNodeWithChildMixin<Re nderBox> {
500 RenderProxyBox(RenderBox child) {
501 this.child = child;
502 }
503
504 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
505 if (child != null)
506 return child.getIntrinsicDimensions(constraints);
507 return super.getIntrinsicDimensions(constraints);
508 }
509
510 void performLayout() {
511 if (child != null) {
512 child.layout(constraints, parentUsesSize: true);
513 size = child.size;
514 } else {
515 performResize();
516 }
517 }
518
519 void hitTestChildren(HitTestResult result, { sky.Point position }) {
520 if (child != null)
521 child.hitTest(result, position: position);
522 else
523 super.hitTestChildren(result, position: position);
524 }
525
526 void paint(RenderNodeDisplayList canvas) {
527 if (child != null)
528 child.paint(canvas);
529 }
530 }
531
532 class RenderSizedBox extends RenderProxyBox {
533 final sky.Size desiredSize;
534
535 RenderSizedBox({
536 RenderBox child,
537 this.desiredSize: const sky.Size.infinite()
538 }) : super(child);
539
540 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
541 return constraints.constrain(desiredSize);
542 }
543
544 void performLayout() {
545 size = constraints.constrain(desiredSize);
546 child.layout(new BoxConstraints.tight(size));
547 }
548 }
549
550 class RenderPadding extends RenderBox with RenderNodeWithChildMixin<RenderBox> {
551
552 RenderPadding(EdgeDims padding, RenderBox child) {
553 assert(padding != null);
554 this.padding = padding;
555 this.child = child;
556 }
557
558 EdgeDims _padding;
559 EdgeDims get padding => _padding;
560 void set padding (EdgeDims value) {
561 assert(value != null);
562 if (_padding != value) {
563 _padding = value;
564 markNeedsLayout();
565 }
566 }
567
568 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
569 assert(padding != null);
570 constraints = constraints.deflate(padding);
571 if (child == null)
572 return super.getIntrinsicDimensions(constraints);
573 return child.getIntrinsicDimensions(constraints);
574 }
575
576 void performLayout() {
577 assert(padding != null);
578 BoxConstraints innerConstraints = constraints.deflate(padding);
579 if (child == null) {
580 size = innerConstraints.constrain(
581 new sky.Size(padding.left + padding.right, padding.top + padding.botto m));
582 return;
583 }
584 child.layout(innerConstraints, parentUsesSize: true);
585 assert(child.parentData is BoxParentData);
586 child.parentData.position = new sky.Point(padding.left, padding.top);
587 size = constraints.constrain(new sky.Size(padding.left + child.size.width + padding.right,
588 padding.top + child.size.height + padding.bottom));
589 }
590
591 void paint(RenderNodeDisplayList canvas) {
592 if (child != null)
593 canvas.paintChild(child, child.parentData.position);
594 }
595
596 void hitTestChildren(HitTestResult result, { sky.Point position }) {
597 if (child != null) {
598 assert(child.parentData is BoxParentData);
599 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi tion, child.size);
600 if (childBounds.contains(position)) {
601 child.hitTest(result, position: new sky.Point(position.x - child.parentD ata.position.x,
602 position.y - child.parentD ata.position.y));
603 }
604 }
605 }
606
607 }
608
609 // This must be immutable, because we won't notice when it changes
610 class BoxDecoration {
611 const BoxDecoration({
612 this.backgroundColor
613 });
614
615 final int backgroundColor;
616 }
617
618 class RenderDecoratedBox extends RenderProxyBox {
619
620 RenderDecoratedBox({
621 BoxDecoration decoration,
622 RenderBox child
623 }) : _decoration = decoration, super(child);
624
625 BoxDecoration _decoration;
626 BoxDecoration get decoration => _decoration;
627 void set decoration (BoxDecoration value) {
628 if (value == _decoration)
629 return;
630 _decoration = value;
631 markNeedsPaint();
632 }
633
634 void paint(RenderNodeDisplayList canvas) {
635 assert(size.width != null);
636 assert(size.height != null);
637
638 if (_decoration == null)
639 return;
640
641 if (_decoration.backgroundColor != null) {
642 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor;
643 canvas.drawRect(new sky.Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
644 }
645 super.paint(canvas);
646 }
647
648 }
649
650
651 // RENDER VIEW LAYOUT MANAGER
652
653 class ViewConstraints {
654
655 const ViewConstraints({
656 this.width: 0.0, this.height: 0.0, this.orientation: null
657 });
658
659 final double width;
660 final double height;
661 final int orientation;
662
663 }
664
665 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> {
666
667 RenderView({
668 RenderBox child,
669 this.timeForRotation: const Duration(microseconds: 83333)
670 }) {
671 this.child = child;
672 }
673
674 sky.Size _size = new sky.Size(0.0, 0.0);
675 double get width => _size.width;
676 double get height => _size.height;
677
678 int _orientation; // 0..3
679 int get orientation => _orientation;
680 Duration timeForRotation;
681
682 ViewConstraints get constraints => super.constraints as ViewConstraints;
683 bool get sizedByParent => true;
684 void performResize() {
685 if (constraints.orientation != _orientation) {
686 if (_orientation != null && child != null)
687 child.rotate(oldAngle: _orientation, newAngle: constraints.orientation, time: timeForRotation);
688 _orientation = constraints.orientation;
689 }
690 _size = new sky.Size(constraints.width, constraints.height);
691 assert(_size.height < double.INFINITY);
692 assert(_size.width < double.INFINITY);
693 }
694 void performLayout() {
695 if (child != null) {
696 child.layout(new BoxConstraints.tight(_size));
697 assert(child.size.width == width);
698 assert(child.size.height == height);
699 }
700 }
701
702 void rotate({ int oldAngle, int newAngle, Duration time }) {
703 assert(false); // nobody tells the screen to rotate, the whole rotate() danc e is started from our performResize()
704 }
705
706 bool hitTest(HitTestResult result, { sky.Point position }) {
707 if (child != null) {
708 sky.Rect childBounds = new sky.Rect.fromSize(child.size);
709 if (childBounds.contains(position))
710 child.hitTest(result, position: position);
711 }
712 result.add(this);
713 return true;
714 }
715
716 void paint(RenderNodeDisplayList canvas) {
717 if (child != null)
718 canvas.paintChild(child, new sky.Point(0.0, 0.0));
719 }
720
721 void paintFrame() {
722 RenderNode._debugDoingPaint = true;
723 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height);
724 paint(canvas);
725 sky.view.picture = canvas.endRecording();
726 RenderNode._debugDoingPaint = false;
727 }
728
729 }
730
731 // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS
732 abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare ntDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRend erNodeMixin<ChildType, ParentDataType> {
733
734 void defaultHitTestChildren(HitTestResult result, { sky.Point position }) {
735 // the x, y parameters have the top left of the node's box as the origin
736 ChildType child = lastChild;
737 while (child != null) {
738 assert(child.parentData is BoxParentData);
739 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi tion, child.size);
740 if (childBounds.contains(position)) {
741 if (child.hitTest(result, position: new sky.Point(position.x - child.par entData.position.x,
742 position.y - child.par entData.position.y)))
743 break;
744 }
745 child = child.parentData.previousSibling;
746 }
747 }
748
749 void defaultPaint(RenderNodeDisplayList canvas) {
750 RenderBox child = firstChild;
751 while (child != null) {
752 assert(child.parentData is BoxParentData);
753 canvas.paintChild(child, child.parentData.position);
754 child = child.parentData.nextSibling;
755 }
756 }
757 }
758
759 // BLOCK LAYOUT MANAGER
760
761 class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render Box> { }
762
763 class RenderBlock extends RenderBox with ContainerRenderNodeMixin<RenderBox, Blo ckParentData>,
764 RenderBoxContainerDefaultsMixin<RenderB ox, BlockParentData> {
765 // lays out RenderBox children in a vertical stack
766 // uses the maximum width provided by the parent
767 // sizes itself to the height of its child stack
768
769 void setParentData(RenderBox child) {
770 if (child.parentData is! BlockParentData)
771 child.parentData = new BlockParentData();
772 }
773
774 // override this to report what dimensions you would have if you
775 // were laid out with the given constraints this can walk the tree
776 // if it must, but it should be as cheap as possible; just get the
777 // dimensions and nothing else (e.g. don't calculate hypothetical
778 // child positions if they're not needed to determine dimensions)
779 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
780 double height = 0.0;
781 double width = constraints.constrainWidth(constraints.maxWidth);
782 assert(width < double.INFINITY);
783 RenderBox child = firstChild;
784 BoxConstraints innerConstraints = new BoxConstraints(minWidth: width,
785 maxWidth: width);
786 while (child != null) {
787 height += child.getIntrinsicDimensions(innerConstraints).height;
788 assert(child.parentData is BlockParentData);
789 child = child.parentData.nextSibling;
790 }
791
792 return new sky.Size(width, constraints.constrainHeight(height));
793 }
794
795 void performLayout() {
796 assert(constraints is BoxConstraints);
797 double width = constraints.constrainWidth(constraints.maxWidth);
798 double y = 0.0;
799 RenderBox child = firstChild;
800 while (child != null) {
801 child.layout(new BoxConstraints(minWidth: width, maxWidth: width), parentU sesSize: true);
802 assert(child.parentData is BlockParentData);
803 child.parentData.position = new sky.Point(0.0, y);
804 y += child.size.height;
805 child = child.parentData.nextSibling;
806 }
807 size = new sky.Size(width, constraints.constrainHeight(y));
808 assert(size.width < double.INFINITY);
809 assert(size.height < double.INFINITY);
810 }
811
812 void hitTestChildren(HitTestResult result, { sky.Point position }) {
813 defaultHitTestChildren(result, position: position);
814 }
815
816 void paint(RenderNodeDisplayList canvas) {
817 defaultPaint(canvas);
818 }
819
820 }
821
822 // FLEXBOX LAYOUT MANAGER
823
824 class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<Rend erBox> {
825 int flex;
826 void merge(FlexBoxParentData other) {
827 if (other.flex != null)
828 flex = other.flex;
829 super.merge(other);
830 }
831 }
832
833 enum FlexDirection { Horizontal, Vertical }
834
835 class RenderFlex extends RenderBox with ContainerRenderNodeMixin<RenderBox, Flex BoxParentData>,
836 RenderBoxContainerDefaultsMixin<RenderBo x, BlockParentData> {
837 // lays out RenderBox children using flexible layout
838
839 RenderFlex({
840 FlexDirection direction: FlexDirection.Horizontal
841 }) : _direction = direction;
842
843 FlexDirection _direction;
844 FlexDirection get direction => _direction;
845 void set direction (FlexDirection value) {
846 if (_direction != value) {
847 _direction = value;
848 markNeedsLayout();
849 }
850 }
851
852 void setParentData(RenderBox child) {
853 if (child.parentData is! FlexBoxParentData)
854 child.parentData = new FlexBoxParentData();
855 }
856
857 bool get sizedByParent => true;
858 void performResize() {
859 size = _constraints.constrain(new sky.Size(_constraints.maxWidth, _constrain ts.maxHeight));
860 assert(size.height < double.INFINITY);
861 assert(size.width < double.INFINITY);
862 }
863
864 int _getFlex(RenderBox child) {
865 assert(child.parentData is FlexBoxParentData);
866 return child.parentData.flex != null ? child.parentData.flex : 0;
867 }
868
869 void performLayout() {
870 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib le Lengths
871 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f ree space
872 int totalFlex = 0;
873 assert(constraints != null);
874 double freeSpace = (_direction == FlexDirection.Horizontal) ? constraints.ma xWidth : constraints.maxHeight;
875 RenderBox child = firstChild;
876 while (child != null) {
877 int flex = _getFlex(child);
878 if (flex > 0) {
879 totalFlex += child.parentData.flex;
880 } else {
881 BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constrai nts.maxHeight,
882 maxWidth: constrain ts.maxWidth);
883 child.layout(innerConstraints, parentUsesSize: true);
884 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.size.width : child.size.height;
885 }
886 child = child.parentData.nextSibling;
887 }
888
889 // Steps 4-5. Distribute remaining space to flexible children.
890 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
891 double usedSpace = 0.0;
892 child = firstChild;
893 while (child != null) {
894 int flex = _getFlex(child);
895 if (flex > 0) {
896 double spaceForChild = spacePerFlex * flex;
897 BoxConstraints innerConstraints;
898 switch (_direction) {
899 case FlexDirection.Horizontal:
900 innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeig ht,
901 minWidth: spaceForChild,
902 maxWidth: spaceForChild);
903 break;
904 case FlexDirection.Vertical:
905 innerConstraints = new BoxConstraints(minHeight: spaceForChild,
906 maxHeight: spaceForChild,
907 maxWidth: constraints.maxWidth );
908 break;
909 }
910 child.layout(innerConstraints, parentUsesSize: true);
911 }
912
913 // For now, center the flex items in the cross direction
914 switch (_direction) {
915 case FlexDirection.Horizontal:
916 child.parentData.position = new sky.Point(usedSpace, size.height / 2.0 - child.size.height / 2.0);
917 usedSpace += child.size.width;
918 break;
919 case FlexDirection.Vertical:
920 child.parentData.position = new sky.Point(size.width / 2.0 - child.siz e.width / 2.0, usedSpace);
921 usedSpace += child.size.height;
922 break;
923 }
924 child = child.parentData.nextSibling;
925 }
926 }
927
928 void hitTestChildren(HitTestResult result, { sky.Point position }) {
929 defaultHitTestChildren(result, position: position);
930 }
931
932 void paint(RenderNodeDisplayList canvas) {
933 defaultPaint(canvas);
934 }
935 }
936
937 class RenderInline extends RenderNode {
938 String data;
939
940 RenderInline(this.data);
941 }
942
943 class RenderParagraph extends RenderBox {
944
945 RenderParagraph({
946 String text,
947 int color
948 }) : _color = color {
949 _layoutRoot.rootElement = _document.createElement('p');
950 this.text = text;
951 }
952
953 final sky.Document _document = new sky.Document();
954 final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot();
955
956 String get text => (_layoutRoot.rootElement.firstChild as sky.Text).data;
957 void set text (String value) {
958 _layoutRoot.rootElement.setChild(_document.createText(value));
959 markNeedsLayout();
960 }
961
962 int _color = 0xFF000000;
963 int get color => _color;
964 void set color (int value) {
965 if (_color != value) {
966 _color = value;
967 markNeedsPaint();
968 }
969 }
970
971 sky.Size getIntrinsicDimensions(BoxConstraints constraints) {
972 assert(false);
973 return null;
974 // we don't currently support this for RenderParagraph
975 }
976
977 void performLayout() {
978 _layoutRoot.maxWidth = constraints.maxWidth;
979 _layoutRoot.minWidth = constraints.minWidth;
980 _layoutRoot.minHeight = constraints.minHeight;
981 _layoutRoot.maxHeight = constraints.maxHeight;
982 _layoutRoot.layout();
983 size = constraints.constrain(new sky.Size(_layoutRoot.rootElement.width, _la youtRoot.rootElement.height));
984 }
985
986 void paint(RenderNodeDisplayList canvas) {
987 // _layoutRoot.rootElement.style['color'] = 'rgba(' + ...color... + ')';
988 _layoutRoot.paint(canvas);
989 }
990
991 // we should probably expose a way to do precise (inter-glpyh) hit testing
992
993 }
OLDNEW
« no previous file with comments | « sky/sdk/lib/framework/fn2.dart ('k') | sky/sdk/lib/framework/rendering/render_block.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698