| 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 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 int newAngle, // 0..3 | 214 int newAngle, // 0..3 |
| 215 Duration time | 215 Duration time |
| 216 }) { } | 216 }) { } |
| 217 | 217 |
| 218 | 218 |
| 219 // HIT TESTING | 219 // HIT TESTING |
| 220 | 220 |
| 221 bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) { | 221 bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) { |
| 222 // override this if you have children, to hand it to the appropriate child | 222 // override this if you have children, to hand it to the appropriate child |
| 223 // override this if you want to do anything with the pointer event | 223 // override this if you want to do anything with the pointer event |
| 224 return false; |
| 224 } | 225 } |
| 225 | 226 |
| 226 | 227 |
| 227 // PAINTING | 228 // PAINTING |
| 228 | 229 |
| 229 static bool _debugDoingPaint = false; | 230 static bool _debugDoingPaint = false; |
| 230 void markNeedsPaint() { | 231 void markNeedsPaint() { |
| 231 assert(!_debugDoingPaint); | 232 assert(!_debugDoingPaint); |
| 232 // TODO(abarth): It's very redunant to call this for every node in the | 233 // TODO(abarth): It's very redunant to call this for every node in the |
| 233 // render tree during layout. We should instead compute a summary bit and | 234 // render tree during layout. We should instead compute a summary bit and |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 | 419 |
| 419 double constrainHeight(double height) { | 420 double constrainHeight(double height) { |
| 420 return clamp(min: minHeight, max: maxHeight, value: height); | 421 return clamp(min: minHeight, max: maxHeight, value: height); |
| 421 } | 422 } |
| 422 } | 423 } |
| 423 | 424 |
| 424 class BoxDimensions { | 425 class BoxDimensions { |
| 425 const BoxDimensions({this.width, this.height}); | 426 const BoxDimensions({this.width, this.height}); |
| 426 | 427 |
| 427 BoxDimensions.withConstraints( | 428 BoxDimensions.withConstraints( |
| 428 BoxConstraints constraints, {double width: 0.0, double height: 0.0}) { | 429 BoxConstraints constraints, {double width: 0.0, double height: 0.0}) |
| 429 this.width = constraints.constrainWidth(width); | 430 : width = constraints.constrainWidth(width), |
| 430 this.height = constraints.constrainHeight(height); | 431 height = constraints.constrainHeight(height); |
| 431 } | |
| 432 | 432 |
| 433 final double width; | 433 final double width; |
| 434 final double height; | 434 final double height; |
| 435 } | 435 } |
| 436 | 436 |
| 437 class BoxParentData extends ParentData { | 437 class BoxParentData extends ParentData { |
| 438 double x = 0.0; | 438 double x = 0.0; |
| 439 double y = 0.0; | 439 double y = 0.0; |
| 440 } | 440 } |
| 441 | 441 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 573 void paintFrame() { | 573 void paintFrame() { |
| 574 RenderNode._debugDoingPaint = true; | 574 RenderNode._debugDoingPaint = true; |
| 575 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); | 575 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); |
| 576 paint(canvas); | 576 paint(canvas); |
| 577 sky.view.picture = canvas.endRecording(); | 577 sky.view.picture = canvas.endRecording(); |
| 578 RenderNode._debugDoingPaint = false; | 578 RenderNode._debugDoingPaint = false; |
| 579 } | 579 } |
| 580 | 580 |
| 581 } | 581 } |
| 582 | 582 |
| 583 // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS |
| 584 abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
ntDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRend
erNodeMixin<ChildType, ParentDataType> { |
| 585 bool defaultHandlePointer(sky.PointerEvent event, double x, double y) { |
| 586 // the x, y parameters have the top left of the node's box as the origin |
| 587 ChildType child = lastChild; |
| 588 while (child != null) { |
| 589 assert(child.parentData is BoxParentData); |
| 590 if ((x >= child.parentData.x) && (x < child.parentData.x + child.width) && |
| 591 (y >= child.parentData.y) && (y < child.parentData.y + child.height))
{ |
| 592 if (child.handlePointer(event, x: x-child.parentData.x, y: y-child.paren
tData.y)) |
| 593 return true; |
| 594 break; |
| 595 } |
| 596 child = child.parentData.previousSibling; |
| 597 } |
| 598 return false; |
| 599 } |
| 600 |
| 601 void defaultPaint(RenderNodeDisplayList canvas) { |
| 602 RenderBox child = firstChild; |
| 603 while (child != null) { |
| 604 assert(child.parentData is BoxParentData); |
| 605 canvas.paintChild(child, child.parentData.x, child.parentData.y); |
| 606 child = child.parentData.nextSibling; |
| 607 } |
| 608 } |
| 609 } |
| 583 | 610 |
| 584 // BLOCK LAYOUT MANAGER | 611 // BLOCK LAYOUT MANAGER |
| 585 | 612 |
| 586 class EdgeDims { | 613 class EdgeDims { |
| 587 // used for e.g. padding | 614 // used for e.g. padding |
| 588 const EdgeDims(this.top, this.right, this.bottom, this.left); | 615 const EdgeDims(this.top, this.right, this.bottom, this.left); |
| 589 final double top; | 616 final double top; |
| 590 final double right; | 617 final double right; |
| 591 final double bottom; | 618 final double bottom; |
| 592 final double left; | 619 final double left; |
| 593 operator ==(EdgeDims other) => (top == other.top) || | 620 operator ==(EdgeDims other) => (top == other.top) || |
| 594 (right == other.right) || | 621 (right == other.right) || |
| 595 (bottom == other.bottom) || | 622 (bottom == other.bottom) || |
| 596 (left == other.left); | 623 (left == other.left); |
| 597 } | 624 } |
| 598 | 625 |
| 599 class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render
Box> { } | 626 class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render
Box> { } |
| 600 | 627 |
| 601 class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende
rBox, BlockParentData> { | 628 class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende
rBox, BlockParentData>, |
| 629 RenderBoxContainerDefaultsMixi
n<RenderBox, BlockParentData> { |
| 602 // lays out RenderBox children in a vertical stack | 630 // lays out RenderBox children in a vertical stack |
| 603 // uses the maximum width provided by the parent | 631 // uses the maximum width provided by the parent |
| 604 // sizes itself to the height of its child stack | 632 // sizes itself to the height of its child stack |
| 605 | 633 |
| 606 RenderBlock({ | 634 RenderBlock({ |
| 607 BoxDecoration decoration, | 635 BoxDecoration decoration, |
| 608 EdgeDims padding: const EdgeDims(0.0, 0.0, 0.0, 0.0) | 636 EdgeDims padding: const EdgeDims(0.0, 0.0, 0.0, 0.0) |
| 609 }) : super(decoration), _padding = padding; | 637 }) : super(decoration), _padding = padding; |
| 610 | 638 |
| 611 EdgeDims _padding; | 639 EdgeDims _padding; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 627 // were laid out with the given constraints this can walk the tree | 655 // were laid out with the given constraints this can walk the tree |
| 628 // if it must, but it should be as cheap as possible; just get the | 656 // if it must, but it should be as cheap as possible; just get the |
| 629 // dimensions and nothing else (e.g. don't calculate hypothetical | 657 // dimensions and nothing else (e.g. don't calculate hypothetical |
| 630 // child positions if they're not needed to determine dimensions) | 658 // child positions if they're not needed to determine dimensions) |
| 631 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { | 659 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
| 632 double outerHeight = _padding.top + _padding.bottom; | 660 double outerHeight = _padding.top + _padding.bottom; |
| 633 double outerWidth = constraints.constrainWidth(constraints.maxWidth); | 661 double outerWidth = constraints.constrainWidth(constraints.maxWidth); |
| 634 assert(outerWidth < double.INFINITY); | 662 assert(outerWidth < double.INFINITY); |
| 635 double innerWidth = outerWidth - (_padding.left + _padding.right); | 663 double innerWidth = outerWidth - (_padding.left + _padding.right); |
| 636 RenderBox child = _firstChild; | 664 RenderBox child = _firstChild; |
| 637 BoxConstraints constraints = new BoxConstraints(minWidth: innerWidth, | 665 BoxConstraints innerConstraints = new BoxConstraints(minWidth: innerWidth, |
| 638 maxWidth: innerWidth); | 666 maxWidth: innerWidth); |
| 639 while (child != null) { | 667 while (child != null) { |
| 640 outerHeight += child.getIntrinsicDimensions(constraints).height; | 668 outerHeight += child.getIntrinsicDimensions(innerConstraints).height; |
| 641 assert(child.parentData is BlockParentData); | 669 assert(child.parentData is BlockParentData); |
| 642 child = child.parentData.nextSibling; | 670 child = child.parentData.nextSibling; |
| 643 } | 671 } |
| 644 | 672 |
| 645 return new BoxDimensions(width: outerWidth, | 673 return new BoxDimensions(width: outerWidth, |
| 646 height: constraints.constrainHeight(outerHeight)); | 674 height: constraints.constrainHeight(outerHeight)); |
| 647 } | 675 } |
| 648 | 676 |
| 649 double _minHeight; // value cached from parent for relayout call | 677 double _minHeight; // value cached from parent for relayout call |
| 650 double _maxHeight; // value cached from parent for relayout call | 678 double _maxHeight; // value cached from parent for relayout call |
| (...skipping 25 matching lines...) Expand all Loading... |
| 676 child.parentData.x = _padding.left; | 704 child.parentData.x = _padding.left; |
| 677 child.parentData.y = y; | 705 child.parentData.y = y; |
| 678 y += child.height; | 706 y += child.height; |
| 679 child = child.parentData.nextSibling; | 707 child = child.parentData.nextSibling; |
| 680 } | 708 } |
| 681 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight)
; | 709 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight)
; |
| 682 layoutDone(); | 710 layoutDone(); |
| 683 } | 711 } |
| 684 | 712 |
| 685 bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) { | 713 bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) { |
| 686 // the x, y parameters have the top left of the node's box as the origin | 714 return defaultHandlePointer(event, x, y) || super.handlePointer(event, x: x,
y: y); |
| 687 RenderBox child = _lastChild; | |
| 688 while (child != null) { | |
| 689 assert(child.parentData is BlockParentData); | |
| 690 if ((x >= child.parentData.x) && (x < child.parentData.x + child.width) && | |
| 691 (y >= child.parentData.y) && (y < child.parentData.y + child.height))
{ | |
| 692 if (child.handlePointer(event, x: x-child.parentData.x, y: y-child.paren
tData.y)) | |
| 693 return true; | |
| 694 break; | |
| 695 } | |
| 696 child = child.parentData.previousSibling; | |
| 697 } | |
| 698 return super.handlePointer(event, x: x, y: y); | |
| 699 } | 715 } |
| 700 | 716 |
| 701 void paint(RenderNodeDisplayList canvas) { | 717 void paint(RenderNodeDisplayList canvas) { |
| 702 super.paint(canvas); | 718 super.paint(canvas); |
| 703 RenderBox child = _firstChild; | 719 defaultPaint(canvas); |
| 704 while (child != null) { | |
| 705 assert(child.parentData is BlockParentData); | |
| 706 canvas.paintChild(child, child.parentData.x, child.parentData.y); | |
| 707 child = child.parentData.nextSibling; | |
| 708 } | |
| 709 } | 720 } |
| 710 | 721 |
| 711 } | 722 } |
| 712 | 723 |
| 713 class FlexBoxParentData extends BoxParentData { | 724 // FLEXBOX LAYOUT MANAGER |
| 725 |
| 726 class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<Rend
erBox> { |
| 714 int flex; | 727 int flex; |
| 715 void merge(FlexBoxParentData other) { | 728 void merge(FlexBoxParentData other) { |
| 716 if (other.flex != null) | 729 if (other.flex != null) |
| 717 flex = other.flex; | 730 flex = other.flex; |
| 718 super.merge(other); | 731 super.merge(other); |
| 719 } | 732 } |
| 720 } | 733 } |
| 721 | 734 |
| 722 enum FlexDirection { Row, Column } | 735 enum FlexDirection { Horizontal, Vertical } |
| 723 | 736 |
| 724 // TODO(ianh): FlexBox | 737 class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render
Box, FlexBoxParentData>, |
| 738 RenderBoxContainerDefaultsMixin
<RenderBox, BlockParentData> { |
| 739 // lays out RenderBox children using flexible layout |
| 725 | 740 |
| 741 RenderFlex({ |
| 742 BoxDecoration decoration, |
| 743 FlexDirection direction: FlexDirection.Horizontal |
| 744 }) : super(decoration), _direction = direction; |
| 745 |
| 746 FlexDirection _direction; |
| 747 FlexDirection get direction => _direction; |
| 748 void set direction (FlexDirection value) { |
| 749 if (_direction != value) { |
| 750 _direction = value; |
| 751 markNeedsLayout(); |
| 752 } |
| 753 } |
| 754 |
| 755 void setParentData(RenderBox child) { |
| 756 if (child.parentData is! FlexBoxParentData) |
| 757 child.parentData = new FlexBoxParentData(); |
| 758 } |
| 759 |
| 760 BoxConstraints _constraints; // value cached from parent for relayout call |
| 761 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
| 762 if (relayoutSubtreeRoot != null) |
| 763 saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
| 764 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo
ot; |
| 765 _constraints = constraints; |
| 766 width = _constraints.constrainWidth(_constraints.maxWidth); |
| 767 height = _constraints.constrainHeight(_constraints.maxHeight); |
| 768 assert(height < double.INFINITY); |
| 769 assert(width < double.INFINITY); |
| 770 internalLayout(relayoutSubtreeRoot); |
| 771 } |
| 772 |
| 773 void relayout() { |
| 774 internalLayout(this); |
| 775 } |
| 776 |
| 777 int _getFlex(RenderBox child) { |
| 778 assert(child.parentData is FlexBoxParentData); |
| 779 return (child.parentData.flex != null ? child.parentData.flex : 0); |
| 780 } |
| 781 |
| 782 void internalLayout(RenderNode relayoutSubtreeRoot) { |
| 783 // 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 |
| 785 int numFlexibleChildren = 0; |
| 786 int totalFlex = 0; |
| 787 assert(_constraints != null); |
| 788 double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.m
axWidth : _constraints.maxHeight; |
| 789 RenderBox child = _firstChild; |
| 790 while (child != null) { |
| 791 int flex = _getFlex(child); |
| 792 if (flex > 0) { |
| 793 numFlexibleChildren++; |
| 794 totalFlex += child.parentData.flex; |
| 795 } else { |
| 796 BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.
maxHeight, |
| 797 maxWidth: _constraints.m
axWidth); |
| 798 child.layout(constraints, |
| 799 relayoutSubtreeRoot: relayoutSubtreeRoot); |
| 800 freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : ch
ild.height; |
| 801 } |
| 802 child = child.parentData.nextSibling; |
| 803 } |
| 804 |
| 805 // Steps 4-5. Distribute remaining space to flexible children. |
| 806 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; |
| 807 double usedSpace = 0.0; |
| 808 child = _firstChild; |
| 809 while (child != null) { |
| 810 int flex = _getFlex(child); |
| 811 if (flex > 0) { |
| 812 double spaceForChild = spacePerFlex * flex; |
| 813 BoxConstraints constraints; |
| 814 switch (_direction) { |
| 815 case FlexDirection.Horizontal: |
| 816 constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, |
| 817 minWidth: spaceForChild, |
| 818 maxWidth: spaceForChild); |
| 819 break; |
| 820 case FlexDirection.Vertical: |
| 821 constraints = new BoxConstraints(minHeight: spaceForChild, |
| 822 maxHeight: spaceForChild, |
| 823 maxWidth: _constraints.maxWidth); |
| 824 break; |
| 825 } |
| 826 child.layout(constraints, relayoutSubtreeRoot: relayoutSubtreeRoot); |
| 827 } |
| 828 |
| 829 // For now, center the flex items in the cross direction |
| 830 switch (_direction) { |
| 831 case FlexDirection.Horizontal: |
| 832 child.parentData.x = usedSpace; |
| 833 usedSpace += child.width; |
| 834 child.parentData.y = height / 2 - child.height / 2; |
| 835 break; |
| 836 case FlexDirection.Vertical: |
| 837 child.parentData.y = usedSpace; |
| 838 usedSpace += child.height; |
| 839 child.parentData.x = width / 2 - child.width / 2; |
| 840 break; |
| 841 } |
| 842 child = child.parentData.nextSibling; |
| 843 } |
| 844 layoutDone(); |
| 845 } |
| 846 |
| 847 bool handlePointer(sky.PointerEvent event, { double x: 0.0, double y: 0.0 }) { |
| 848 return defaultHandlePointer(event, x, y) || super.handlePointer(event, x: x,
y: y); |
| 849 } |
| 850 |
| 851 void paint(RenderNodeDisplayList canvas) { |
| 852 super.paint(canvas); |
| 853 defaultPaint(canvas); |
| 854 } |
| 855 } |
| 726 | 856 |
| 727 // SCAFFOLD LAYOUT MANAGER | 857 // SCAFFOLD LAYOUT MANAGER |
| 728 | 858 |
| 729 // a sample special-purpose layout manager | 859 // a sample special-purpose layout manager |
| 730 | 860 |
| 731 class ScaffoldBox extends RenderBox { | 861 class ScaffoldBox extends RenderBox { |
| 732 | 862 |
| 733 ScaffoldBox(this.toolbar, this.body, this.statusbar, this.drawer) { | 863 ScaffoldBox(this.toolbar, this.body, this.statusbar, this.drawer) { |
| 734 assert(body != null); | 864 assert(body != null); |
| 735 } | 865 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 791 canvas.paintChild(body, (body.parentData as BoxParentData).x, (body.parentDa
ta as BoxParentData).y); | 921 canvas.paintChild(body, (body.parentData as BoxParentData).x, (body.parentDa
ta as BoxParentData).y); |
| 792 if (statusbar != null) | 922 if (statusbar != null) |
| 793 canvas.paintChild(statusbar, (statusbar.parentData as BoxParentData).x, (s
tatusbar.parentData as BoxParentData).y); | 923 canvas.paintChild(statusbar, (statusbar.parentData as BoxParentData).x, (s
tatusbar.parentData as BoxParentData).y); |
| 794 if (toolbar != null) | 924 if (toolbar != null) |
| 795 canvas.paintChild(toolbar, (toolbar.parentData as BoxParentData).x, (toolb
ar.parentData as BoxParentData).y); | 925 canvas.paintChild(toolbar, (toolbar.parentData as BoxParentData).x, (toolb
ar.parentData as BoxParentData).y); |
| 796 if (drawer != null) | 926 if (drawer != null) |
| 797 canvas.paintChild(drawer, (drawer.parentData as BoxParentData).x, (drawer.
parentData as BoxParentData).y); | 927 canvas.paintChild(drawer, (drawer.parentData as BoxParentData).x, (drawer.
parentData as BoxParentData).y); |
| 798 } | 928 } |
| 799 | 929 |
| 800 } | 930 } |
| OLD | NEW |