提交 950b3d40 编写于 作者: C Collin Jackson

Extend Sky's RenderFlex with intrinsic sizes and compute cross-size height

Renders interactive_flex and stocks demos fine. Could use more testing,
so I'm going to work on that after I land this.

R=abarth@chromium.org, abarth

Review URL: https://codereview.chromium.org/1173493003
上级 42892c73
......@@ -14,19 +14,27 @@ class RenderSolidColorBox extends RenderDecoratedBox {
super(decoration: new BoxDecoration(backgroundColor: backgroundColor));
double getMinIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth(desiredSize.width);
return constraints.constrainHeight(
this.desiredSize == Size.infinite ? 0.0 : desiredSize.width
);
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return constraints.constrainWidth(desiredSize.width);
return constraints.constrainWidth(
this.desiredSize == Size.infinite ? 0.0 : desiredSize.width
);
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight(desiredSize.height);
return constraints.constrainHeight(
this.desiredSize == Size.infinite ? 0.0 : desiredSize.height
);
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return constraints.constrainHeight(desiredSize.height);
return constraints.constrainHeight(
this.desiredSize == Size.infinite ? 0.0 : desiredSize.height
);
}
void performLayout() {
......
......@@ -27,7 +27,7 @@ class StockMenu extends Component {
items: [
[new Text('Add stock')],
[new Text('Remove stock')],
// [new FlexExpandingChild(new Text('Autorefresh')), checkbox],
[new FlexExpandingChild(new Text('Autorefresh')), checkbox],
],
level: 4
),
......
......@@ -16,7 +16,7 @@ class PopupMenuItem extends Component {
constraints: const BoxConstraints(minWidth: 112.0),
padding: const EdgeDims.all(16.0),
// TODO(abarth): opacity: opacity,
child: new BlockContainer(children: children)
child: new FlexContainer(children: children)
);
}
}
......@@ -556,13 +556,12 @@ class RenderImage extends RenderBox {
markNeedsLayout();
}
void performLayout() {
Size _sizeForConstraints(BoxConstraints innerConstraints) {
// If there's no image, we can't size ourselves automatically
if (_image == null) {
double width = requestedSize.width == null ? 0.0 : requestedSize.width;
double height = requestedSize.height == null ? 0.0 : requestedSize.height;
size = constraints.constrain(new Size(width, height));
return;
return constraints.constrain(new Size(width, height));
}
// If neither height nor width are specified, use inherent image dimensions
......@@ -570,19 +569,43 @@ class RenderImage extends RenderBox {
// maintain the aspect ratio
if (requestedSize.width == null) {
if (requestedSize.height == null) {
size = constraints.constrain(new Size(_image.width.toDouble(), _image.height.toDouble()));
return constraints.constrain(new Size(_image.width.toDouble(), _image.height.toDouble()));
} else {
double width = requestedSize.height * _image.width / _image.height;
size = constraints.constrain(new Size(width, requestedSize.height));
return constraints.constrain(new Size(width, requestedSize.height));
}
} else if (requestedSize.height == null) {
double height = requestedSize.width * _image.height / _image.width;
size = constraints.constrain(new Size(requestedSize.width, height));
return constraints.constrain(new Size(requestedSize.width, height));
} else {
size = constraints.constrain(requestedSize);
return constraints.constrain(requestedSize);
}
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
if (requestedSize.width == null && requestedSize.height == null)
return constraints.constrainWidth(0.0);
return _sizeForConstraints(constraints).width;
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return _sizeForConstraints(constraints).width;
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
if (requestedSize.width == null && requestedSize.height == null)
return constraints.constrainHeight(0.0);
return _sizeForConstraints(constraints).height;
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return _sizeForConstraints(constraints).height;
}
void performLayout() {
size = _sizeForConstraints(constraints);
}
void paint(RenderObjectDisplayList canvas) {
if (_image == null) return;
bool needsScale = size.width != _image.width || size.height != _image.height;
......@@ -797,7 +820,7 @@ class RenderDecoratedBox extends RenderProxyBox {
assert(size.height != null);
if (_decoration.backgroundColor != null || _decoration.boxShadow != null ||
_deocration.gradient != null) {
_decoration.gradient != null) {
Rect rect = new Rect.fromLTRB(0.0, 0.0, size.width, size.height);
if (_decoration.borderRadius == null)
canvas.drawRect(rect, _backgroundPaint);
......
......@@ -27,6 +27,8 @@ enum FlexJustifyContent {
spaceAround,
}
typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexBoxParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, FlexBoxParentData> {
// lays out RenderBox children using flexible layout
......@@ -59,35 +61,168 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
child.parentData = new FlexBoxParentData();
}
// We don't currently support this for RenderFlex
double _getIntrinsicSize({ BoxConstraints constraints,
FlexDirection sizingDirection,
_ChildSizingFunction childSize }) {
// http://www.w3.org/TR/2015/WD-css-flexbox-1-20150514/#intrinsic-sizes
if (_direction == sizingDirection) {
// INTRINSIC MAIN SIZE
// Intrinsic main size is the smallest size the flex container can take
// while maintaining the min/max-content contributions of its flex items.
BoxConstraints childConstraints;
switch(_direction) {
case FlexDirection.horizontal:
childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
break;
case FlexDirection.vertical:
childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
break;
}
double totalFlex = 0.0;
double inflexibleSpace = 0.0;
double maxFlexFractionSoFar = 0.0;
RenderBox child = firstChild;
while (child != null) {
int flex = _getFlex(child);
totalFlex += flex;
if (flex > 0) {
double flexFraction = childSize(child, childConstraints) / _getFlex(child);
maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction);
} else {
inflexibleSpace += childSize(child, childConstraints);
}
child = child.parentData.nextSibling;
}
double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace;
// Ensure that we don't violate the given constraints with our result
switch(_direction) {
case FlexDirection.horizontal:
return constraints.constrainWidth(mainSize);
case FlexDirection.vertical:
return constraints.constrainHeight(mainSize);
}
} else {
// INTRINSIC CROSS SIZE
// The spec wants us to perform layout into the given available main-axis
// space and return the cross size. That's too expensive, so instead we
// size inflexible children according to their max intrinsic size in the
// main direction and use those constraints to determine their max
// intrinsic size in the cross direction. We don't care if the caller
// asked for max or min -- the answer is always computed using the
// max size in the main direction.
double availableMainSpace;
BoxConstraints childConstraints;
switch(_direction) {
case FlexDirection.horizontal:
childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
availableMainSpace = innerConstraints.maxWidth;
break;
case FlexDirection.vertical:
childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
availableMainSpace = innerConstraints.maxHeight;
break;
}
// Get inflexible space using the max in the main direction
int totalFlex = 0;
double inflexibleSpace = 0.0;
double maxCrossSize = 0.0;
RenderBox child = firstChild;
while (child != null) {
int flex = _getFlex(child);
totalFlex += flex;
double mainSize;
double crossSize;
if (flex == 0) {
switch (_direction) {
case FlexDirection.horizontal:
mainSize = child.getMaxIntrinsicWidth(childConstraints);
BoxConstraints widthConstraints =
new BoxConstraints(minWidth: mainSize, maxWidth: mainSize);
crossSize = child.getMaxIntrinsicHeight(widthConstraints);
break;
case FlexDirection.vertical:
mainSize = child.getMaxIntrinsicHeight(childConstraints);
BoxConstraints heightConstraints =
new BoxConstraints(minWidth: mainSize, maxWidth: mainSize);
crossSize = child.getMaxIntrinsicWidth(heightConstraints);
break;
}
inflexibleSpace += mainSize;
maxCrossSize = math.max(maxCrossSize, crossSize);
}
child = child.parentData.nextSibling;
}
// Determine the spacePerFlex by allocating the remaining available space
double spacePerFlex = (availableMainSpace - inflexibleSpace) / totalFlex;
// Size remaining items, find the maximum cross size
child = firstChild;
while (child != null) {
int flex = _getFlex(child);
if (flex > 0) {
double childMainSize = spacePerFlex * flex;
double crossSize;
switch (_direction) {
case FlexDirection.horizontal:
BoxConstraints childConstraints =
new BoxConstraints(minWidth: childMainSize, maxWidth: childMainSize);
crossSize = child.getMaxIntrinsicHeight(childConstraints);
break;
case FlexDirection.vertical:
BoxConstraints childConstraints =
new BoxConstraints(minHeight: childMainSize, maxHeight: childMainSize);
crossSize = child.getMaxIntrinsicWidth(childConstraints);
break;
}
maxCrossSize = math.max(maxCrossSize, crossSize);
}
child = child.parentData.nextSibling;
}
// Ensure that we don't violate the given constraints with our result
switch(_direction) {
case FlexDirection.horizontal:
return innerConstraints.constrainHeight(maxCrossSize);
case FlexDirection.vertical:
return innerConstraints.constrainWidth(maxCrossSize);
}
}
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
assert(false);
return constraints.constrainWidth(0.0);
return _getIntrinsicSize(
constraints: constraints,
sizingDirection: FlexDirection.horizontal,
childSize: (c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints)
);
}
// We don't currently support this for RenderFlex
double getMaxIntrinsicWidth(BoxConstraints constraints) {
assert(false);
return constraints.constrainWidth(0.0);
return _getIntrinsicSize(
constraints: constraints,
sizingDirection: FlexDirection.horizontal,
childSize: (c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints)
);
}
// We don't currently support this for RenderFlex
double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(false);
return constraints.constrainHeight(0.0);
return _getIntrinsicSize(
constraints: constraints,
sizingDirection: FlexDirection.vertical,
childSize: (c, innerConstraints) => c.getMinIntrinsicHeight(innerConstraints)
);
}
// We don't currently support this for RenderFlex
double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(false);
return constraints.constrainHeight(0.0);
}
bool get sizedByParent => true;
void performResize() {
size = constraints.constrain(new Size(constraints.maxWidth, constraints.maxHeight));
assert(size.height < double.INFINITY);
assert(size.width < double.INFINITY);
return _getIntrinsicSize(
constraints: constraints,
sizingDirection: FlexDirection.vertical,
childSize: (c, innerConstraints) => c.getMaxIntrinsicHeight(innerConstraints));
}
int _getFlex(RenderBox child) {
......@@ -101,7 +236,9 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
int totalFlex = 0;
int totalChildren = 0;
assert(constraints != null);
double freeSpace = (_direction == FlexDirection.horizontal) ? constraints.maxWidth : constraints.maxHeight;
final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.maxWidth : constraints.maxHeight;
double crossSize = 0.0; // This will be determined after laying out the children
double freeSpace = mainSize;
RenderBox child = firstChild;
while (child != null) {
totalChildren++;
......@@ -131,15 +268,19 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight,
minWidth: spaceForChild,
maxWidth: spaceForChild);
child.layout(innerConstraints, parentUsesSize: true);
usedSpace += child.size.width;
crossSize = math.max(crossSize, child.size.height);
break;
case FlexDirection.vertical:
innerConstraints = new BoxConstraints(minHeight: spaceForChild,
maxHeight: spaceForChild,
maxWidth: constraints.maxWidth);
child.layout(innerConstraints, parentUsesSize: true);
usedSpace += child.size.height;
crossSize = math.max(crossSize, child.size.width);
break;
}
child.layout(innerConstraints, parentUsesSize: true);
usedSpace += _direction == FlexDirection.horizontal ? child.size.width : child.size.height;
}
child = child.parentData.nextSibling;
}
......@@ -172,6 +313,15 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
break;
}
switch (_direction) {
case FlexDirection.horizontal:
size = constraints.constrain(new Size(mainSize, crossSize));
break;
case FlexDirection.vertical:
size = constraints.constrain(new Size(crossSize, mainSize));
break;
}
// Position elements. For now, center the flex items in the cross direction
double mainDimPosition = leadingSpace;
child = firstChild;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册