OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 import 'node.dart'; | 5 import 'node.dart'; |
6 import 'dart:sky' as sky; | 6 import 'dart:sky' as sky; |
7 | 7 |
8 // ABSTRACT LAYOUT | 8 // ABSTRACT LAYOUT |
9 | 9 |
10 class ParentData { | 10 class ParentData { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 assert(child.parentData != null); | 67 assert(child.parentData != null); |
68 child.parentData.detach(); | 68 child.parentData.detach(); |
69 super.dropChild(child); | 69 super.dropChild(child); |
70 } | 70 } |
71 | 71 |
72 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>(); | 72 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>(); |
73 static bool _debugDoingLayout = false; | 73 static bool _debugDoingLayout = false; |
74 bool _needsLayout = true; | 74 bool _needsLayout = true; |
75 bool get needsLayout => _needsLayout; | 75 bool get needsLayout => _needsLayout; |
76 RenderNode _relayoutSubtreeRoot; | 76 RenderNode _relayoutSubtreeRoot; |
77 void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) { | 77 dynamic _constraints; |
78 _relayoutSubtreeRoot = relayoutSubtreeRoot; | 78 dynamic get constraints => _constraints; |
79 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot._relayoutSubtree
Root == null); | |
80 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || (pa
rent is RenderNode && _relayoutSubtreeRoot == parent._relayoutSubtreeRoot)); | |
81 } | |
82 bool debugAncestorsAlreadyMarkedNeedsLayout() { | 79 bool debugAncestorsAlreadyMarkedNeedsLayout() { |
83 if (_relayoutSubtreeRoot == null) | 80 if (_relayoutSubtreeRoot == null) |
84 return true; | 81 return true; // we haven't yet done layout even once, so there's nothing f
or us to do |
85 RenderNode node = this; | 82 RenderNode node = this; |
86 while (node != _relayoutSubtreeRoot) { | 83 while (node != _relayoutSubtreeRoot) { |
87 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); | 84 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); |
88 assert(node.parent != null); | 85 assert(node.parent != null); |
89 node = node.parent as RenderNode; | 86 node = node.parent as RenderNode; |
90 if (!node._needsLayout) | 87 if (!node._needsLayout) |
91 return false; | 88 return false; |
92 } | 89 } |
93 assert(node._relayoutSubtreeRoot == null); | 90 assert(node._relayoutSubtreeRoot == node); |
94 return true; | 91 return true; |
95 } | 92 } |
96 void markNeedsLayout() { | 93 void markNeedsLayout() { |
97 assert(!_debugDoingLayout); | 94 assert(!_debugDoingLayout); |
98 assert(!_debugDoingPaint); | 95 assert(!_debugDoingPaint); |
99 if (_needsLayout) { | 96 if (_needsLayout) { |
100 assert(debugAncestorsAlreadyMarkedNeedsLayout()); | 97 assert(debugAncestorsAlreadyMarkedNeedsLayout()); |
101 return; | 98 return; |
102 } | 99 } |
103 _needsLayout = true; | 100 _needsLayout = true; |
104 if (_relayoutSubtreeRoot != null) { | 101 assert(_relayoutSubtreeRoot != null); |
| 102 if (_relayoutSubtreeRoot != this) { |
105 assert(parent is RenderNode); | 103 assert(parent is RenderNode); |
106 parent.markNeedsLayout(); | 104 parent.markNeedsLayout(); |
107 } else { | 105 } else { |
108 _nodesNeedingLayout.add(this); | 106 _nodesNeedingLayout.add(this); |
109 } | 107 } |
110 } | 108 } |
111 static void flushLayout() { | 109 static void flushLayout() { |
112 _debugDoingLayout = true; | 110 _debugDoingLayout = true; |
113 List<RenderNode> dirtyNodes = _nodesNeedingLayout; | 111 List<RenderNode> dirtyNodes = _nodesNeedingLayout; |
114 _nodesNeedingLayout = new List<RenderNode>(); | 112 _nodesNeedingLayout = new List<RenderNode>(); |
115 dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { | 113 dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { |
116 if (node._needsLayout && node.attached) | 114 if (node._needsLayout && node.attached) |
117 node._doLayout(); | 115 node._doLayout(); |
118 }); | 116 }); |
119 _debugDoingLayout = false; | 117 _debugDoingLayout = false; |
120 } | 118 } |
121 void _doLayout() { | 119 void _doLayout() { |
122 try { | 120 try { |
123 assert(_relayoutSubtreeRoot == null); | 121 assert(_relayoutSubtreeRoot == this); |
124 relayout(); | 122 performLayout(); |
125 } catch (e, stack) { | 123 } catch (e, stack) { |
126 print('Exception raised during layout of ${this}: ${e}'); | 124 print('Exception raised during layout of ${this}: ${e}'); |
127 print(stack); | 125 print(stack); |
128 return; | 126 return; |
129 } | 127 } |
130 assert(!_needsLayout); // check that the relayout() method marked us "not di
rty" | 128 assert(!_needsLayout); // check that the relayout() method marked us "not di
rty" |
131 } | 129 } |
132 /* // this method's signature is subclass-specific, but will exist in | 130 void layout(dynamic constraints, { bool parentUsesSize: false }) { |
133 // some form in all subclasses: | 131 RenderNode relayoutSubtreeRoot; |
134 void layout({arguments..., RenderNode relayoutSubtreeRoot}) { | 132 if (!parentUsesSize || sizedByParent || parent is! RenderNode) |
135 bool childArgumentsChanged = ...; // true if arguments we're going to pas
s to the children are different than last time, false otherwise | 133 relayoutSubtreeRoot = this; |
136 if (this node has an opinion about its size, e.g. because it autosizes ba
sed on kids, or has an intrinsic dimension) { | 134 else |
137 if (relayoutSubtreeRoot != null) { | 135 relayoutSubtreeRoot = parent._relayoutSubtreeRoot; |
138 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); | 136 if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _r
elayoutSubtreeRoot) |
139 // for each child, if we are going to size ourselves around them: | 137 return; |
140 if (child.needsLayout || childArgumentsChanged) | 138 _constraints = constraints; |
141 child.layout(... relayoutSubtreeRoot: relayoutSubtreeRoot); | 139 _relayoutSubtreeRoot = relayoutSubtreeRoot; |
142 width = ...; | 140 if (sizedByParent) |
143 height = ...; | 141 performResize(); |
144 } else { | 142 performLayout(); |
145 saveRelayoutSubtreeRoot(null); // you can skip this if there's no way
you would ever have called saveRelayoutSubtreeRoot() before | 143 _needsLayout = false; |
146 // we're the root of the relayout subtree | 144 markNeedsPaint(); |
147 // for each child, if we are going to size ourselves around them: | 145 } |
148 if (child.needsLayout || childArgumentsChanged) | 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) |
149 child.layout(... relayoutSubtreeRoot: this); | 147 void performResize(); // set the local dimensions, using only the constraints
(only called if sizedByParent is true) |
150 width = ...; | 148 void performLayout(); |
151 height = ...; | |
152 } | |
153 } else { | |
154 // we're sizing ourselves exclusively on input from the parent (argumen
ts to this function) | |
155 // ignore relayoutSubtreeRoot | |
156 saveRelayoutSubtreeRoot(null); // you can skip this if there's no way y
ou would ever have called saveRelayoutSubtreeRoot() before | |
157 width = ...; // based on input from arguments only | |
158 height = ...; // based on input from arguments only | |
159 } | |
160 // for each child whose size we'll ignore when deciding ours: | |
161 if (child.needsLayout || childArgumentsChanged) | |
162 child.layout(... relayoutSubtreeRoot: null); // or just omit relayoutSu
btreeRoot | |
163 layoutDone(); | |
164 return; | |
165 } | |
166 */ | |
167 void relayout() { | |
168 // Override this to perform relayout without your parent's | 149 // Override this to perform relayout without your parent's |
169 // involvement. | 150 // involvement. |
170 // | 151 // |
171 // This is what is called after the first layout(), if you mark | 152 // This is called during layout. If sizedByParent is true, then |
172 // yourself dirty and don't have a _relayoutSubtreeRoot set; in | 153 // performLayout() should not change your dimensions, only do that |
173 // other words, either if your parent doesn't care what size you | 154 // in performResize(). If sizedByParent is false, then set both |
174 // are (and thus didn't pass a relayoutSubtreeRoot to your | 155 // your dimensions and do your children's layout here. |
175 // layout() method) or if you sized yourself entirely based on | |
176 // what your parents told you, and not based on your children (and | |
177 // thus you never called saveRelayoutSubtreeRoot()). | |
178 // | 156 // |
179 // In the former case, you can resize yourself here at will. In | 157 // When calling layout() on your children, pass in |
180 // the latter case, just leave your dimensions unchanged. | 158 // "parentUsesSize: true" if your size or layout is dependent on |
181 // | 159 // your child's size. |
182 // If _relayoutSubtreeRoot is set (i.e. you called saveRelayout- | |
183 // SubtreeRoot() in your layout(), with a relayoutSubtreeRoot | |
184 // argument that was non-null), then if you mark yourself as dirty | |
185 // then we'll tell that subtree root instead, and the layout will | |
186 // occur via the layout() tree rather than starting from this | |
187 // relayout() method. | |
188 // | |
189 // when calling children's layout() methods, skip any children | |
190 // that have needsLayout == false unless the arguments you are | |
191 // passing in have changed since the last time | |
192 assert(_relayoutSubtreeRoot == null); | |
193 layoutDone(); | |
194 } | |
195 void layoutDone({bool needsPaint: true}) { | |
196 // make sure to call this at the end of your layout() or relayout() | |
197 _needsLayout = false; | |
198 if (needsPaint) | |
199 markNeedsPaint(); | |
200 } | |
201 | 160 |
202 // when the parent has rotated (e.g. when the screen has been turned | 161 // when the parent has rotated (e.g. when the screen has been turned |
203 // 90 degrees), immediately prior to layout() being called for the | 162 // 90 degrees), immediately prior to layout() being called for the |
204 // new dimensions, rotate() is called with the old and new angles. | 163 // new dimensions, rotate() is called with the old and new angles. |
205 // The next time paint() is called, the coordinate space will have | 164 // The next time paint() is called, the coordinate space will have |
206 // been rotated N quarter-turns clockwise, where: | 165 // been rotated N quarter-turns clockwise, where: |
207 // N = newAngle-oldAngle | 166 // N = newAngle-oldAngle |
208 // ...but the rendering is expected to remain the same, pixel for | 167 // ...but the rendering is expected to remain the same, pixel for |
209 // pixel, on the output device. Then, the layout() method or | 168 // pixel, on the output device. Then, the layout() method or |
210 // equivalent will be invoked. | 169 // equivalent will be invoked. |
211 | 170 |
212 void rotate({ | 171 void rotate({ |
213 int oldAngle, // 0..3 | 172 int oldAngle, // 0..3 |
214 int newAngle, // 0..3 | 173 int newAngle, // 0..3 |
215 Duration time | 174 Duration time |
216 }) { } | 175 }) { } |
217 | 176 |
218 | 177 |
219 // PAINTING | 178 // PAINTING |
220 | 179 |
221 static bool _debugDoingPaint = false; | 180 static bool _debugDoingPaint = false; |
222 void markNeedsPaint() { | 181 void markNeedsPaint() { |
223 assert(!_debugDoingPaint); | 182 assert(!_debugDoingPaint); |
224 // TODO(abarth): It's very redunant to call this for every node in the | 183 // TODO(abarth): It's very redundant to call this for every node in the |
225 // render tree during layout. We should instead compute a summary bit and | 184 // render tree during layout. We should instead compute a summary bit and |
226 // call it once at the end of layout. | 185 // call it once at the end of layout. |
227 sky.view.scheduleFrame(); | 186 sky.view.scheduleFrame(); |
228 } | 187 } |
229 void paint(RenderNodeDisplayList canvas) { } | 188 void paint(RenderNodeDisplayList canvas) { } |
230 | 189 |
231 | 190 |
232 // HIT TESTING | 191 // HIT TESTING |
233 | 192 |
234 void handlePointer(sky.PointerEvent event) { | 193 void handlePointer(sky.PointerEvent event) { |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
516 | 475 |
517 // override this to report what dimensions you would have if you | 476 // override this to report what dimensions you would have if you |
518 // were laid out with the given constraints this can walk the tree | 477 // were laid out with the given constraints this can walk the tree |
519 // if it must, but it should be as cheap as possible; just get the | 478 // if it must, but it should be as cheap as possible; just get the |
520 // dimensions and nothing else (e.g. don't calculate hypothetical | 479 // dimensions and nothing else (e.g. don't calculate hypothetical |
521 // child positions if they're not needed to determine dimensions) | 480 // child positions if they're not needed to determine dimensions) |
522 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { | 481 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
523 return new BoxDimensions.withConstraints(constraints); | 482 return new BoxDimensions.withConstraints(constraints); |
524 } | 483 } |
525 | 484 |
526 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { | 485 BoxConstraints get constraints => super.constraints as BoxConstraints; |
| 486 void performResize() { |
| 487 // default behaviour for subclasses that have sizedByParent = true |
527 width = constraints.constrainWidth(0.0); | 488 width = constraints.constrainWidth(0.0); |
528 height = constraints.constrainHeight(0.0); | 489 height = constraints.constrainHeight(0.0); |
529 layoutDone(); | 490 assert(height < double.INFINITY); |
| 491 assert(width < double.INFINITY); |
| 492 } |
| 493 void performLayout() { |
| 494 // descendants have to either override performLayout() to set both |
| 495 // width and height and lay out children, or, set sizedByParent to |
| 496 // true so that performResize()'s logic above does its thing. |
| 497 assert(sizedByParent); |
530 } | 498 } |
531 | 499 |
532 bool hitTest(HitTestResult result, { double x, double y }) { | 500 bool hitTest(HitTestResult result, { double x, double y }) { |
533 hitTestChildren(result, x: x, y: y); | 501 hitTestChildren(result, x: x, y: y); |
534 result.add(this); | 502 result.add(this); |
535 return true; | 503 return true; |
536 } | 504 } |
537 void hitTestChildren(HitTestResult result, { double x, double y }) { } | 505 void hitTestChildren(HitTestResult result, { double x, double y }) { } |
538 | 506 |
539 double width; | 507 double width; |
(...skipping 19 matching lines...) Expand all Loading... |
559 } | 527 } |
560 | 528 |
561 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { | 529 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
562 assert(padding != null); | 530 assert(padding != null); |
563 constraints = constraints.deflate(padding); | 531 constraints = constraints.deflate(padding); |
564 if (child == null) | 532 if (child == null) |
565 return super.getIntrinsicDimensions(constraints); | 533 return super.getIntrinsicDimensions(constraints); |
566 return child.getIntrinsicDimensions(constraints); | 534 return child.getIntrinsicDimensions(constraints); |
567 } | 535 } |
568 | 536 |
569 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { | 537 void performLayout() { |
570 assert(padding != null); | 538 assert(padding != null); |
571 constraints = constraints.deflate(padding); | 539 BoxConstraints innerConstraints = constraints.deflate(padding); |
572 if (child == null) { | 540 if (child == null) { |
573 width = constraints.constrainWidth(padding.left + padding.right); | 541 width = innerConstraints.constrainWidth(padding.left + padding.right); |
574 height = constraints.constrainHeight(padding.top + padding.bottom); | 542 height = innerConstraints.constrainHeight(padding.top + padding.bottom); |
575 return; | 543 return; |
576 } | 544 } |
577 if (relayoutSubtreeRoot != null) | 545 child.layout(innerConstraints, parentUsesSize: true); |
578 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); | |
579 else | |
580 relayoutSubtreeRoot = this; | |
581 child.layout(constraints, relayoutSubtreeRoot: relayoutSubtreeRoot); | |
582 assert(child.parentData is BoxParentData); | 546 assert(child.parentData is BoxParentData); |
583 child.parentData.x = padding.left; | 547 child.parentData.x = padding.left; |
584 child.parentData.y = padding.top; | 548 child.parentData.y = padding.top; |
585 width = constraints.constrainWidth(padding.left + child.width + padding.righ
t); | 549 width = constraints.constrainWidth(padding.left + child.width + padding.righ
t); |
586 height = constraints.constrainHeight(padding.top + child.height + padding.bo
ttom); | 550 height = constraints.constrainHeight(padding.top + child.height + padding.bo
ttom); |
587 } | 551 } |
588 | 552 |
589 void paint(RenderNodeDisplayList canvas) { | 553 void paint(RenderNodeDisplayList canvas) { |
590 if (child != null) | 554 if (child != null) |
591 canvas.paintChild(child, child.parentData.x, child.parentData.y); | 555 canvas.paintChild(child, child.parentData.x, child.parentData.y); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
657 if (_decoration.backgroundColor != null) { | 621 if (_decoration.backgroundColor != null) { |
658 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; | 622 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; |
659 canvas.drawCircle(new sky.Rect()..setLTRB(0.0, 0.0, width, height), paint)
; | 623 canvas.drawCircle(new sky.Rect()..setLTRB(0.0, 0.0, width, height), paint)
; |
660 } | 624 } |
661 } | 625 } |
662 } | 626 } |
663 | 627 |
664 | 628 |
665 // RENDER VIEW LAYOUT MANAGER | 629 // RENDER VIEW LAYOUT MANAGER |
666 | 630 |
| 631 class ViewConstraints { |
| 632 |
| 633 const ViewConstraints({ |
| 634 this.width: 0.0, this.height: 0.0, this.orientation: null |
| 635 }); |
| 636 |
| 637 final double width; |
| 638 final double height; |
| 639 final int orientation; |
| 640 |
| 641 } |
| 642 |
667 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { | 643 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { |
668 | 644 |
669 RenderView({ | 645 RenderView({ |
670 RenderBox child, | 646 RenderBox child, |
671 this.timeForRotation: const Duration(microseconds: 83333) | 647 this.timeForRotation: const Duration(microseconds: 83333) |
672 }) { | 648 }) { |
673 this.child = child; | 649 this.child = child; |
674 } | 650 } |
675 | 651 |
676 double _width; | 652 double _width; |
677 double get width => _width; | 653 double get width => _width; |
678 double _height; | 654 double _height; |
679 double get height => _height; | 655 double get height => _height; |
680 | 656 |
681 int _orientation; // 0..3 | 657 int _orientation; // 0..3 |
682 int get orientation => _orientation; | 658 int get orientation => _orientation; |
683 Duration timeForRotation; | 659 Duration timeForRotation; |
684 | 660 |
685 void layout({ | 661 ViewConstraints get constraints => super.constraints as ViewConstraints; |
686 double newWidth, | 662 bool get sizedByParent => true; |
687 double newHeight, | 663 void performResize() { |
688 int newOrientation | 664 if (constraints.orientation != _orientation) { |
689 }) { | 665 if (_orientation != null && child != null) |
690 if (newOrientation != orientation) { | 666 child.rotate(oldAngle: _orientation, newAngle: constraints.orientation,
time: timeForRotation); |
691 if (orientation != null && child != null) | 667 _orientation = constraints.orientation; |
692 child.rotate(oldAngle: orientation, newAngle: newOrientation, time: time
ForRotation); | |
693 _orientation = newOrientation; | |
694 } | 668 } |
695 if ((newWidth != width) || (newHeight != height)) { | 669 _width = constraints.width; |
696 _width = newWidth; | 670 _height = constraints.height; |
697 _height = newHeight; | 671 assert(height < double.INFINITY); |
698 relayout(); | 672 assert(width < double.INFINITY); |
699 } else { | |
700 layoutDone(); | |
701 } | |
702 } | 673 } |
703 | 674 void performLayout() { |
704 void relayout() { | |
705 if (child != null) { | 675 if (child != null) { |
706 child.layout(new BoxConstraints.tight(width: width, height: height)); | 676 child.layout(new BoxConstraints.tight(width: width, height: height)); |
707 assert(child.width == width); | 677 assert(child.width == width); |
708 assert(child.height == height); | 678 assert(child.height == height); |
709 } | 679 } |
710 layoutDone(); | |
711 } | 680 } |
712 | 681 |
713 void rotate({ int oldAngle, int newAngle, Duration time }) { | 682 void rotate({ int oldAngle, int newAngle, Duration time }) { |
714 assert(false); // nobody tells the screen to rotate, the whole rotate() danc
e is started from our layout() | 683 assert(false); // nobody tells the screen to rotate, the whole rotate() danc
e is started from our performResize() |
715 } | 684 } |
716 | 685 |
717 bool hitTest(HitTestResult result, { double x, double y }) { | 686 bool hitTest(HitTestResult result, { double x, double y }) { |
718 if (child != null && x >= 0.0 && x < child.width && y >= 0.0 && y < child.he
ight) | 687 if (child != null && x >= 0.0 && x < child.width && y >= 0.0 && y < child.he
ight) |
719 child.hitTest(result, x: x, y: y); | 688 child.hitTest(result, x: x, y: y); |
720 result.add(this); | 689 result.add(this); |
721 return true; | 690 return true; |
722 } | 691 } |
723 | 692 |
724 void paint(RenderNodeDisplayList canvas) { | 693 void paint(RenderNodeDisplayList canvas) { |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
798 while (child != null) { | 767 while (child != null) { |
799 outerHeight += child.getIntrinsicDimensions(innerConstraints).height; | 768 outerHeight += child.getIntrinsicDimensions(innerConstraints).height; |
800 assert(child.parentData is BlockParentData); | 769 assert(child.parentData is BlockParentData); |
801 child = child.parentData.nextSibling; | 770 child = child.parentData.nextSibling; |
802 } | 771 } |
803 | 772 |
804 return new BoxDimensions(width: outerWidth, | 773 return new BoxDimensions(width: outerWidth, |
805 height: constraints.constrainHeight(outerHeight)); | 774 height: constraints.constrainHeight(outerHeight)); |
806 } | 775 } |
807 | 776 |
808 BoxConstraints _constraints; // value cached from parent for relayout call | 777 void performLayout() { |
809 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { | 778 assert(constraints is BoxConstraints); |
810 if (relayoutSubtreeRoot != null) | |
811 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); | |
812 else | |
813 relayoutSubtreeRoot = this; | |
814 width = constraints.constrainWidth(constraints.maxWidth); | 779 width = constraints.constrainWidth(constraints.maxWidth); |
815 assert(width < double.INFINITY); | 780 assert(width < double.INFINITY); |
816 _constraints = constraints; | |
817 internalLayout(relayoutSubtreeRoot); | |
818 } | |
819 | |
820 void relayout() { | |
821 internalLayout(this); | |
822 } | |
823 | |
824 void internalLayout(RenderNode relayoutSubtreeRoot) { | |
825 assert(_constraints != null); | |
826 double y = 0.0; | 781 double y = 0.0; |
827 double innerWidth = width; | 782 double innerWidth = width; |
828 RenderBox child = firstChild; | 783 RenderBox child = firstChild; |
829 while (child != null) { | 784 while (child != null) { |
830 child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth
), | 785 child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth
), parentUsesSize: true); |
831 relayoutSubtreeRoot: relayoutSubtreeRoot); | |
832 assert(child.parentData is BlockParentData); | 786 assert(child.parentData is BlockParentData); |
833 child.parentData.x = 0.0; | 787 child.parentData.x = 0.0; |
834 child.parentData.y = y; | 788 child.parentData.y = y; |
835 y += child.height; | 789 y += child.height; |
836 child = child.parentData.nextSibling; | 790 child = child.parentData.nextSibling; |
837 } | 791 } |
838 height = _constraints.constrainHeight(y); | 792 height = constraints.constrainHeight(y); |
839 layoutDone(); | |
840 } | 793 } |
841 | 794 |
842 void hitTestChildren(HitTestResult result, { double x, double y }) { | 795 void hitTestChildren(HitTestResult result, { double x, double y }) { |
843 defaultHitTestChildren(result, x: x, y: y); | 796 defaultHitTestChildren(result, x: x, y: y); |
844 } | 797 } |
845 | 798 |
846 void paint(RenderNodeDisplayList canvas) { | 799 void paint(RenderNodeDisplayList canvas) { |
847 super.paint(canvas); | 800 super.paint(canvas); |
848 defaultPaint(canvas); | 801 defaultPaint(canvas); |
849 } | 802 } |
(...skipping 29 matching lines...) Expand all Loading... |
879 _direction = value; | 832 _direction = value; |
880 markNeedsLayout(); | 833 markNeedsLayout(); |
881 } | 834 } |
882 } | 835 } |
883 | 836 |
884 void setParentData(RenderBox child) { | 837 void setParentData(RenderBox child) { |
885 if (child.parentData is! FlexBoxParentData) | 838 if (child.parentData is! FlexBoxParentData) |
886 child.parentData = new FlexBoxParentData(); | 839 child.parentData = new FlexBoxParentData(); |
887 } | 840 } |
888 | 841 |
889 BoxConstraints _constraints; // value cached from parent for relayout call | 842 bool get sizedByParent => true; |
890 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { | 843 void performResize() { |
891 if (relayoutSubtreeRoot != null) | |
892 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); | |
893 else | |
894 relayoutSubtreeRoot = this; | |
895 _constraints = constraints; | |
896 width = _constraints.constrainWidth(_constraints.maxWidth); | 844 width = _constraints.constrainWidth(_constraints.maxWidth); |
897 height = _constraints.constrainHeight(_constraints.maxHeight); | 845 height = _constraints.constrainHeight(_constraints.maxHeight); |
898 assert(height < double.INFINITY); | 846 assert(height < double.INFINITY); |
899 assert(width < double.INFINITY); | 847 assert(width < double.INFINITY); |
900 internalLayout(relayoutSubtreeRoot); | |
901 } | |
902 | |
903 void relayout() { | |
904 internalLayout(this); | |
905 } | 848 } |
906 | 849 |
907 int _getFlex(RenderBox child) { | 850 int _getFlex(RenderBox child) { |
908 assert(child.parentData is FlexBoxParentData); | 851 assert(child.parentData is FlexBoxParentData); |
909 return child.parentData.flex != null ? child.parentData.flex : 0; | 852 return child.parentData.flex != null ? child.parentData.flex : 0; |
910 } | 853 } |
911 | 854 |
912 void internalLayout(RenderNode relayoutSubtreeRoot) { | 855 void performLayout() { |
913 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib
le Lengths | 856 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib
le Lengths |
914 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f
ree space | 857 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f
ree space |
915 int totalFlex = 0; | 858 int totalFlex = 0; |
916 assert(_constraints != null); | 859 assert(constraints != null); |
917 double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.m
axWidth : _constraints.maxHeight; | 860 double freeSpace = (_direction == FlexDirection.Horizontal) ? constraints.ma
xWidth : constraints.maxHeight; |
918 RenderBox child = firstChild; | 861 RenderBox child = firstChild; |
919 while (child != null) { | 862 while (child != null) { |
920 int flex = _getFlex(child); | 863 int flex = _getFlex(child); |
921 if (flex > 0) { | 864 if (flex > 0) { |
922 totalFlex += child.parentData.flex; | 865 totalFlex += child.parentData.flex; |
923 } else { | 866 } else { |
924 BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.
maxHeight, | 867 BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constrai
nts.maxHeight, |
925 maxWidth: _constraints.m
axWidth); | 868 maxWidth: constrain
ts.maxWidth); |
926 child.layout(constraints, | 869 child.layout(innerConstraints, parentUsesSize: true); |
927 relayoutSubtreeRoot: relayoutSubtreeRoot); | |
928 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : ch
ild.height; | 870 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : ch
ild.height; |
929 } | 871 } |
930 child = child.parentData.nextSibling; | 872 child = child.parentData.nextSibling; |
931 } | 873 } |
932 | 874 |
933 // Steps 4-5. Distribute remaining space to flexible children. | 875 // Steps 4-5. Distribute remaining space to flexible children. |
934 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; | 876 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; |
935 double usedSpace = 0.0; | 877 double usedSpace = 0.0; |
936 child = firstChild; | 878 child = firstChild; |
937 while (child != null) { | 879 while (child != null) { |
938 int flex = _getFlex(child); | 880 int flex = _getFlex(child); |
939 if (flex > 0) { | 881 if (flex > 0) { |
940 double spaceForChild = spacePerFlex * flex; | 882 double spaceForChild = spacePerFlex * flex; |
941 BoxConstraints constraints; | 883 BoxConstraints innerConstraints; |
942 switch (_direction) { | 884 switch (_direction) { |
943 case FlexDirection.Horizontal: | 885 case FlexDirection.Horizontal: |
944 constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, | 886 innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeig
ht, |
945 minWidth: spaceForChild, | 887 minWidth: spaceForChild, |
946 maxWidth: spaceForChild); | 888 maxWidth: spaceForChild); |
947 break; | 889 break; |
948 case FlexDirection.Vertical: | 890 case FlexDirection.Vertical: |
949 constraints = new BoxConstraints(minHeight: spaceForChild, | 891 innerConstraints = new BoxConstraints(minHeight: spaceForChild, |
950 maxHeight: spaceForChild, | 892 maxHeight: spaceForChild, |
951 maxWidth: _constraints.maxWidth); | 893 maxWidth: constraints.maxWidth
); |
952 break; | 894 break; |
953 } | 895 } |
954 child.layout(constraints, relayoutSubtreeRoot: relayoutSubtreeRoot); | 896 child.layout(innerConstraints, parentUsesSize: true); |
955 } | 897 } |
956 | 898 |
957 // For now, center the flex items in the cross direction | 899 // For now, center the flex items in the cross direction |
958 switch (_direction) { | 900 switch (_direction) { |
959 case FlexDirection.Horizontal: | 901 case FlexDirection.Horizontal: |
960 child.parentData.x = usedSpace; | 902 child.parentData.x = usedSpace; |
961 usedSpace += child.width; | 903 usedSpace += child.width; |
962 child.parentData.y = height / 2 - child.height / 2; | 904 child.parentData.y = height / 2.0 - child.height / 2.0; |
963 break; | 905 break; |
964 case FlexDirection.Vertical: | 906 case FlexDirection.Vertical: |
965 child.parentData.y = usedSpace; | 907 child.parentData.y = usedSpace; |
966 usedSpace += child.height; | 908 usedSpace += child.height; |
967 child.parentData.x = width / 2 - child.width / 2; | 909 child.parentData.x = width / 2.0 - child.width / 2.0; |
968 break; | 910 break; |
969 } | 911 } |
970 child = child.parentData.nextSibling; | 912 child = child.parentData.nextSibling; |
971 } | 913 } |
972 layoutDone(); | |
973 } | 914 } |
974 | 915 |
975 void hitTestChildren(HitTestResult result, { double x, double y }) { | 916 void hitTestChildren(HitTestResult result, { double x, double y }) { |
976 defaultHitTestChildren(result, x: x, y: y); | 917 defaultHitTestChildren(result, x: x, y: y); |
977 } | 918 } |
978 | 919 |
979 void paint(RenderNodeDisplayList canvas) { | 920 void paint(RenderNodeDisplayList canvas) { |
980 super.paint(canvas); | 921 super.paint(canvas); |
981 defaultPaint(canvas); | 922 defaultPaint(canvas); |
982 } | 923 } |
983 } | 924 } |
OLD | NEW |