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 |