// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:sky' as sky; import 'box.dart'; import 'node.dart'; class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin { int flex; void merge(FlexBoxParentData other) { if (other.flex != null) flex = other.flex; super.merge(other); } } enum FlexDirection { Horizontal, Vertical } class RenderFlex extends RenderBox with ContainerRenderNodeMixin, RenderBoxContainerDefaultsMixin { // lays out RenderBox children using flexible layout RenderFlex({ FlexDirection direction: FlexDirection.Horizontal }) : _direction = direction; FlexDirection _direction; FlexDirection get direction => _direction; void set direction (FlexDirection value) { if (_direction != value) { _direction = value; markNeedsLayout(); } } void setParentData(RenderBox child) { if (child.parentData is! FlexBoxParentData) child.parentData = new FlexBoxParentData(); } bool get sizedByParent => true; void performResize() { size = constraints.constrain(new sky.Size(constraints.maxWidth, constraints.maxHeight)); assert(size.height < double.INFINITY); assert(size.width < double.INFINITY); } int _getFlex(RenderBox child) { assert(child.parentData is FlexBoxParentData); return child.parentData.flex != null ? child.parentData.flex : 0; } void performLayout() { // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths // Steps 1-3. Determine used flex factor, size inflexible items, calculate free space int totalFlex = 0; assert(constraints != null); double freeSpace = (_direction == FlexDirection.Horizontal) ? constraints.maxWidth : constraints.maxHeight; RenderBox child = firstChild; while (child != null) { int flex = _getFlex(child); if (flex > 0) { totalFlex += child.parentData.flex; } else { BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, maxWidth: constraints.maxWidth); child.layout(innerConstraints, parentUsesSize: true); freeSpace -= (_direction == FlexDirection.Horizontal) ? child.size.width : child.size.height; } child = child.parentData.nextSibling; } // Steps 4-5. Distribute remaining space to flexible children. double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; double usedSpace = 0.0; child = firstChild; while (child != null) { int flex = _getFlex(child); if (flex > 0) { double spaceForChild = spacePerFlex * flex; BoxConstraints innerConstraints; switch (_direction) { case FlexDirection.Horizontal: innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, minWidth: spaceForChild, maxWidth: spaceForChild); break; case FlexDirection.Vertical: innerConstraints = new BoxConstraints(minHeight: spaceForChild, maxHeight: spaceForChild, maxWidth: constraints.maxWidth); break; } child.layout(innerConstraints, parentUsesSize: true); } // For now, center the flex items in the cross direction switch (_direction) { case FlexDirection.Horizontal: child.parentData.position = new sky.Point(usedSpace, size.height / 2.0 - child.size.height / 2.0); usedSpace += child.size.width; break; case FlexDirection.Vertical: child.parentData.position = new sky.Point(size.width / 2.0 - child.size.width / 2.0, usedSpace); usedSpace += child.size.height; break; } child = child.parentData.nextSibling; } } void hitTestChildren(HitTestResult result, { sky.Point position }) { defaultHitTestChildren(result, position: position); } void paint(RenderNodeDisplayList canvas) { defaultPaint(canvas); } }