OLD | NEW |
1 library layout; | 1 library layout; |
2 | 2 |
3 // This version of layout.dart is an update to the other one, this one using new
APIs. | 3 // This version of layout.dart is an update to the other one, this one using new
APIs. |
4 // It will not work in a stock Sky setup currently. | 4 // It will not work in a stock Sky setup currently. |
5 | 5 |
6 import 'node.dart'; | 6 import 'node.dart'; |
7 | 7 |
8 import 'dart:sky' as sky; | 8 import 'dart:sky' as sky; |
9 | 9 |
10 // ABSTRACT LAYOUT | 10 // ABSTRACT LAYOUT |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 } | 72 } |
73 | 73 |
74 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>(); | 74 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>(); |
75 static bool _debugDoingLayout = false; | 75 static bool _debugDoingLayout = false; |
76 bool _needsLayout = true; | 76 bool _needsLayout = true; |
77 bool get needsLayout => _needsLayout; | 77 bool get needsLayout => _needsLayout; |
78 RenderNode _relayoutSubtreeRoot; | 78 RenderNode _relayoutSubtreeRoot; |
79 void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) { | 79 void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) { |
80 _relayoutSubtreeRoot = relayoutSubtreeRoot; | 80 _relayoutSubtreeRoot = relayoutSubtreeRoot; |
81 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot._relayoutSubtree
Root == null); | 81 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot._relayoutSubtree
Root == null); |
82 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || _re
layoutSubtreeRoot == parent._relayoutSubtreeRoot); | 82 assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || (pa
rent is RenderNode && _relayoutSubtreeRoot == parent._relayoutSubtreeRoot)); |
83 } | 83 } |
84 bool debugAncestorsAlreadyMarkedNeedsLayout() { | 84 bool debugAncestorsAlreadyMarkedNeedsLayout() { |
85 if (_relayoutSubtreeRoot == null) | 85 if (_relayoutSubtreeRoot == null) |
86 return true; | 86 return true; |
87 RenderNode node = this; | 87 RenderNode node = this; |
88 while (node != _relayoutSubtreeRoot) { | 88 while (node != _relayoutSubtreeRoot) { |
89 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); | 89 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); |
90 assert(node.parent != null); | 90 assert(node.parent != null); |
91 node = node.parent as RenderNode; | 91 node = node.parent as RenderNode; |
92 if (!node._needsLayout) | 92 if (!node._needsLayout) |
93 return false; | 93 return false; |
94 } | 94 } |
95 assert(node._relayoutSubtreeRoot == null); | 95 assert(node._relayoutSubtreeRoot == null); |
96 return true; | 96 return true; |
97 } | 97 } |
98 void markNeedsLayout() { | 98 void markNeedsLayout() { |
99 assert(!_debugDoingLayout); | 99 assert(!_debugDoingLayout); |
100 assert(!_debugDoingPaint); | 100 assert(!_debugDoingPaint); |
101 if (_needsLayout) { | 101 if (_needsLayout) { |
102 assert(debugAncestorsAlreadyMarkedNeedsLayout()); | 102 assert(debugAncestorsAlreadyMarkedNeedsLayout()); |
103 return; | 103 return; |
104 } | 104 } |
105 _needsLayout = true; | 105 _needsLayout = true; |
| 106 assert(parent is RenderNode); |
106 if (_relayoutSubtreeRoot != null) | 107 if (_relayoutSubtreeRoot != null) |
107 parent.markNeedsLayout(); | 108 parent.markNeedsLayout(); |
108 else | 109 else |
109 _nodesNeedingLayout.add(this); | 110 _nodesNeedingLayout.add(this); |
110 } | 111 } |
111 static void flushLayout() { | 112 static void flushLayout() { |
112 _debugDoingLayout = true; | 113 _debugDoingLayout = true; |
113 List<RenderNode> dirtyNodes = _nodesNeedingLayout; | 114 List<RenderNode> dirtyNodes = _nodesNeedingLayout; |
114 _nodesNeedingLayout = new List<RenderNode>(); | 115 _nodesNeedingLayout = new List<RenderNode>(); |
115 dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { | 116 dirtyNodes..sort((a, b) => a.depth - b.depth)..forEach((node) { |
(...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 // GENERIC BOX RENDERING | 396 // GENERIC BOX RENDERING |
396 // Anything that has a concept of x, y, width, height is going to derive from th
is | 397 // Anything that has a concept of x, y, width, height is going to derive from th
is |
397 | 398 |
398 class BoxConstraints { | 399 class BoxConstraints { |
399 const BoxConstraints({ | 400 const BoxConstraints({ |
400 this.minWidth: 0.0, | 401 this.minWidth: 0.0, |
401 this.maxWidth: double.INFINITY, | 402 this.maxWidth: double.INFINITY, |
402 this.minHeight: 0.0, | 403 this.minHeight: 0.0, |
403 this.maxHeight: double.INFINITY}); | 404 this.maxHeight: double.INFINITY}); |
404 | 405 |
405 const BoxConstraints.tight({ width: width, height: height }) | 406 const BoxConstraints.tight({ double width: 0.0, double height: 0.0 }) |
406 : minWidth = width, | 407 : minWidth = width, |
407 maxWidth = width, | 408 maxWidth = width, |
408 minHeight = height, | 409 minHeight = height, |
409 maxHeight = height; | 410 maxHeight = height; |
410 | 411 |
411 final double minWidth; | 412 final double minWidth; |
412 final double maxWidth; | 413 final double maxWidth; |
413 final double minHeight; | 414 final double minHeight; |
414 final double maxHeight; | 415 final double maxHeight; |
415 | 416 |
416 double constrainWidth(double width) { | 417 double constrainWidth(double width) { |
417 return clamp(min: minWidth, max: maxWidth, value: width); | 418 return clamp(min: minWidth, max: maxWidth, value: width); |
418 } | 419 } |
419 | 420 |
420 double constrainHeight(double height) { | 421 double constrainHeight(double height) { |
421 return clamp(min: minHeight, max: maxHeight, value: height); | 422 return clamp(min: minHeight, max: maxHeight, value: height); |
422 } | 423 } |
| 424 |
| 425 bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFI
NITY; |
423 } | 426 } |
424 | 427 |
425 class BoxDimensions { | 428 class BoxDimensions { |
426 const BoxDimensions({this.width, this.height}); | 429 const BoxDimensions({ this.width: 0.0, this.height: 0.0 }); |
427 | 430 |
428 BoxDimensions.withConstraints( | 431 BoxDimensions.withConstraints( |
429 BoxConstraints constraints, {double width: 0.0, double height: 0.0}) | 432 BoxConstraints constraints, |
430 : width = constraints.constrainWidth(width), | 433 { double width: 0.0, double height: 0.0 } |
| 434 ) : width = constraints.constrainWidth(width), |
431 height = constraints.constrainHeight(height); | 435 height = constraints.constrainHeight(height); |
432 | 436 |
433 final double width; | 437 final double width; |
434 final double height; | 438 final double height; |
435 } | 439 } |
436 | 440 |
437 class BoxParentData extends ParentData { | 441 class BoxParentData extends ParentData { |
438 double x = 0.0; | 442 double x = 0.0; |
439 double y = 0.0; | 443 double y = 0.0; |
440 } | 444 } |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
654 // override this to report what dimensions you would have if you | 658 // override this to report what dimensions you would have if you |
655 // were laid out with the given constraints this can walk the tree | 659 // were laid out with the given constraints this can walk the tree |
656 // if it must, but it should be as cheap as possible; just get the | 660 // if it must, but it should be as cheap as possible; just get the |
657 // dimensions and nothing else (e.g. don't calculate hypothetical | 661 // dimensions and nothing else (e.g. don't calculate hypothetical |
658 // child positions if they're not needed to determine dimensions) | 662 // child positions if they're not needed to determine dimensions) |
659 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { | 663 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
660 double outerHeight = _padding.top + _padding.bottom; | 664 double outerHeight = _padding.top + _padding.bottom; |
661 double outerWidth = constraints.constrainWidth(constraints.maxWidth); | 665 double outerWidth = constraints.constrainWidth(constraints.maxWidth); |
662 assert(outerWidth < double.INFINITY); | 666 assert(outerWidth < double.INFINITY); |
663 double innerWidth = outerWidth - (_padding.left + _padding.right); | 667 double innerWidth = outerWidth - (_padding.left + _padding.right); |
664 RenderBox child = _firstChild; | 668 RenderBox child = firstChild; |
665 BoxConstraints innerConstraints = new BoxConstraints(minWidth: innerWidth, | 669 BoxConstraints innerConstraints = new BoxConstraints(minWidth: innerWidth, |
666 maxWidth: innerWidth); | 670 maxWidth: innerWidth); |
667 while (child != null) { | 671 while (child != null) { |
668 outerHeight += child.getIntrinsicDimensions(innerConstraints).height; | 672 outerHeight += child.getIntrinsicDimensions(innerConstraints).height; |
669 assert(child.parentData is BlockParentData); | 673 assert(child.parentData is BlockParentData); |
670 child = child.parentData.nextSibling; | 674 child = child.parentData.nextSibling; |
671 } | 675 } |
672 | 676 |
673 return new BoxDimensions(width: outerWidth, | 677 return new BoxDimensions(width: outerWidth, |
674 height: constraints.constrainHeight(outerHeight)); | 678 height: constraints.constrainHeight(outerHeight)); |
(...skipping 14 matching lines...) Expand all Loading... |
689 | 693 |
690 void relayout() { | 694 void relayout() { |
691 internalLayout(this); | 695 internalLayout(this); |
692 } | 696 } |
693 | 697 |
694 void internalLayout(RenderNode relayoutSubtreeRoot) { | 698 void internalLayout(RenderNode relayoutSubtreeRoot) { |
695 assert(_minHeight != null); | 699 assert(_minHeight != null); |
696 assert(_maxHeight != null); | 700 assert(_maxHeight != null); |
697 double y = _padding.top; | 701 double y = _padding.top; |
698 double innerWidth = width - (_padding.left + _padding.right); | 702 double innerWidth = width - (_padding.left + _padding.right); |
699 RenderBox child = _firstChild; | 703 RenderBox child = firstChild; |
700 while (child != null) { | 704 while (child != null) { |
701 child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth
), | 705 child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth
), |
702 relayoutSubtreeRoot: relayoutSubtreeRoot); | 706 relayoutSubtreeRoot: relayoutSubtreeRoot); |
703 assert(child.parentData is BlockParentData); | 707 assert(child.parentData is BlockParentData); |
704 child.parentData.x = _padding.left; | 708 child.parentData.x = _padding.left; |
705 child.parentData.y = y; | 709 child.parentData.y = y; |
706 y += child.height; | 710 y += child.height; |
707 child = child.parentData.nextSibling; | 711 child = child.parentData.nextSibling; |
708 } | 712 } |
709 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight)
; | 713 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight)
; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
769 assert(width < double.INFINITY); | 773 assert(width < double.INFINITY); |
770 internalLayout(relayoutSubtreeRoot); | 774 internalLayout(relayoutSubtreeRoot); |
771 } | 775 } |
772 | 776 |
773 void relayout() { | 777 void relayout() { |
774 internalLayout(this); | 778 internalLayout(this); |
775 } | 779 } |
776 | 780 |
777 int _getFlex(RenderBox child) { | 781 int _getFlex(RenderBox child) { |
778 assert(child.parentData is FlexBoxParentData); | 782 assert(child.parentData is FlexBoxParentData); |
779 return (child.parentData.flex != null ? child.parentData.flex : 0); | 783 return child.parentData.flex != null ? child.parentData.flex : 0; |
780 } | 784 } |
781 | 785 |
782 void internalLayout(RenderNode relayoutSubtreeRoot) { | 786 void internalLayout(RenderNode relayoutSubtreeRoot) { |
783 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib
le Lengths | 787 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib
le Lengths |
784 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f
ree space | 788 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f
ree space |
785 int numFlexibleChildren = 0; | |
786 int totalFlex = 0; | 789 int totalFlex = 0; |
787 assert(_constraints != null); | 790 assert(_constraints != null); |
788 double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.m
axWidth : _constraints.maxHeight; | 791 double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.m
axWidth : _constraints.maxHeight; |
789 RenderBox child = _firstChild; | 792 RenderBox child = firstChild; |
790 while (child != null) { | 793 while (child != null) { |
791 int flex = _getFlex(child); | 794 int flex = _getFlex(child); |
792 if (flex > 0) { | 795 if (flex > 0) { |
793 numFlexibleChildren++; | |
794 totalFlex += child.parentData.flex; | 796 totalFlex += child.parentData.flex; |
795 } else { | 797 } else { |
796 BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.
maxHeight, | 798 BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.
maxHeight, |
797 maxWidth: _constraints.m
axWidth); | 799 maxWidth: _constraints.m
axWidth); |
798 child.layout(constraints, | 800 child.layout(constraints, |
799 relayoutSubtreeRoot: relayoutSubtreeRoot); | 801 relayoutSubtreeRoot: relayoutSubtreeRoot); |
800 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : ch
ild.height; | 802 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : ch
ild.height; |
801 } | 803 } |
802 child = child.parentData.nextSibling; | 804 child = child.parentData.nextSibling; |
803 } | 805 } |
804 | 806 |
805 // Steps 4-5. Distribute remaining space to flexible children. | 807 // Steps 4-5. Distribute remaining space to flexible children. |
806 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; | 808 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; |
807 double usedSpace = 0.0; | 809 double usedSpace = 0.0; |
808 child = _firstChild; | 810 child = firstChild; |
809 while (child != null) { | 811 while (child != null) { |
810 int flex = _getFlex(child); | 812 int flex = _getFlex(child); |
811 if (flex > 0) { | 813 if (flex > 0) { |
812 double spaceForChild = spacePerFlex * flex; | 814 double spaceForChild = spacePerFlex * flex; |
813 BoxConstraints constraints; | 815 BoxConstraints constraints; |
814 switch (_direction) { | 816 switch (_direction) { |
815 case FlexDirection.Horizontal: | 817 case FlexDirection.Horizontal: |
816 constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, | 818 constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, |
817 minWidth: spaceForChild, | 819 minWidth: spaceForChild, |
818 maxWidth: spaceForChild); | 820 maxWidth: spaceForChild); |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
921 canvas.paintChild(body, (body.parentData as BoxParentData).x, (body.parentDa
ta as BoxParentData).y); | 923 canvas.paintChild(body, (body.parentData as BoxParentData).x, (body.parentDa
ta as BoxParentData).y); |
922 if (statusbar != null) | 924 if (statusbar != null) |
923 canvas.paintChild(statusbar, (statusbar.parentData as BoxParentData).x, (s
tatusbar.parentData as BoxParentData).y); | 925 canvas.paintChild(statusbar, (statusbar.parentData as BoxParentData).x, (s
tatusbar.parentData as BoxParentData).y); |
924 if (toolbar != null) | 926 if (toolbar != null) |
925 canvas.paintChild(toolbar, (toolbar.parentData as BoxParentData).x, (toolb
ar.parentData as BoxParentData).y); | 927 canvas.paintChild(toolbar, (toolbar.parentData as BoxParentData).x, (toolb
ar.parentData as BoxParentData).y); |
926 if (drawer != null) | 928 if (drawer != null) |
927 canvas.paintChild(drawer, (drawer.parentData as BoxParentData).x, (drawer.
parentData as BoxParentData).y); | 929 canvas.paintChild(drawer, (drawer.parentData as BoxParentData).x, (drawer.
parentData as BoxParentData).y); |
928 } | 930 } |
929 | 931 |
930 } | 932 } |
OLD | NEW |