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; | |
Hixie
2015/05/23 00:34:20
_lastChild => 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 |