Commit 958de183 authored by Adam Barth's avatar Adam Barth

Merge pull request #1073 from abarth/box_docs

Document and bring sanity to BoxConstraints
parents d810b583 95277953
......@@ -452,7 +452,7 @@ class RenderBoxToRenderSectorAdapter extends RenderBox {
Size getIntrinsicDimensions(BoxConstraints constraints) {
assert(child is RenderSector);
assert(child.parentData is SectorParentData);
assert(!constraints.isInfinite);
assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY);
double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius;
SectorDimensions childDimensions = child.getIntrinsicDimensions(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius);
double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0;
......@@ -464,7 +464,7 @@ class RenderBoxToRenderSectorAdapter extends RenderBox {
size = constraints.constrain(Size.zero);
} else {
assert(child is RenderSector);
assert(!constraints.isInfinite);
assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY);
double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius;
assert(child.parentData is SectorParentData);
child.parentData.radius = innerRadius;
......
......@@ -53,7 +53,7 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
Component createSeparator() {
return new Container(
constraints: const BoxConstraints.expandWidth(maxHeight: 0.0),
constraints: const BoxConstraints.expand(height: 0.0),
margin: const EdgeDims.symmetric(vertical: 10.0, horizontal: 64.0),
decoration: const BoxDecoration(
border: const Border(
......
......@@ -314,7 +314,7 @@ class RenderBlockViewport extends RenderBlockBase {
double getMaxIntrinsicWidth(BoxConstraints constraints) {
if (isVertical)
return _getIntrinsicDimension(constraints, maxCrossAxisDimensionCallback, constraints.constrainWidth);
return _getIntrinsicDimension(constraints, totalExtentCallback, new BoxConstraints(minWidth: minExtent).apply(constraints).constrainWidth);
return _getIntrinsicDimension(constraints, totalExtentCallback, new BoxConstraints(minWidth: minExtent).enforce(constraints).constrainWidth);
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
......@@ -326,7 +326,7 @@ class RenderBlockViewport extends RenderBlockBase {
double getMaxIntrinsicHeight(BoxConstraints constraints) {
if (!isVertical)
return _getIntrinsicDimension(constraints, maxCrossAxisDimensionCallback, constraints.constrainHeight);
return _getIntrinsicDimension(constraints, totalExtentCallback, new BoxConstraints(minHeight: minExtent).apply(constraints).constrainHeight);
return _getIntrinsicDimension(constraints, totalExtentCallback, new BoxConstraints(minHeight: minExtent).enforce(constraints).constrainHeight);
}
// We don't override computeDistanceToActualBaseline(), because we
......
......@@ -23,7 +23,22 @@ class _DebugSize extends Size {
final bool _canBeUsedByParent;
}
/// Immutable layout constraints for box layout
///
/// A size respects a BoxConstraints if, and only if, all of the following
/// relations hold:
///
/// * `minWidth <= size.width <= constraints.maxWidth`
/// * `minHeight <= size.height <= maxHeight`
///
/// The constraints themselves must satisfy these relations:
///
/// * `0.0 <= minWidth <= maxWidth <= double.INFINITY`
/// * `0.0 <= minHeight <= maxHeight <= double.INFINITY`
///
/// Note: `double.INFINITY` is a legal value for each constraint.
class BoxConstraints extends Constraints {
/// Constructs box constraints with the given constraints
const BoxConstraints({
this.minWidth: 0.0,
this.maxWidth: double.INFINITY,
......@@ -31,12 +46,19 @@ class BoxConstraints extends Constraints {
this.maxHeight: double.INFINITY
});
final double minWidth;
final double maxWidth;
final double minHeight;
final double maxHeight;
/// Constructs box constraints that is respected only by the given size
BoxConstraints.tight(Size size)
: minWidth = size.width,
maxWidth = size.width,
minHeight = size.height,
maxHeight = size.height;
/// Constructs box constraints that require the given width or height
const BoxConstraints.tightFor({
double width,
double height
......@@ -45,43 +67,41 @@ class BoxConstraints extends Constraints {
minHeight = height != null ? height : 0.0,
maxHeight = height != null ? height : double.INFINITY;
/// Constructs box constraints that forbid sizes larger than the given size
BoxConstraints.loose(Size size)
: minWidth = 0.0,
maxWidth = size.width,
minHeight = 0.0,
maxHeight = size.height;
const BoxConstraints.expandWidth({
this.maxHeight: double.INFINITY
}): minWidth = double.INFINITY,
maxWidth = double.INFINITY,
minHeight = 0.0;
const BoxConstraints.expandHeight({
this.maxWidth: double.INFINITY
}): minWidth = 0.0,
minHeight = double.INFINITY,
maxHeight = double.INFINITY;
static const BoxConstraints expand = const BoxConstraints(
minWidth: double.INFINITY,
maxWidth: double.INFINITY,
minHeight: double.INFINITY,
maxHeight: double.INFINITY
);
/// Constructs box constraints that expand to fill another box contraints
///
/// If width or height is given, the constraints will require exactly the
/// given value in the given dimension.
const BoxConstraints.expand({
double width,
double height
}): minWidth = width != null ? width : double.INFINITY,
maxWidth = width != null ? width : double.INFINITY,
minHeight = height != null ? height : double.INFINITY,
maxHeight = height != null ? height : double.INFINITY;
/// Returns new box constraints that are smaller by the given edge dimensions
BoxConstraints deflate(EdgeDims edges) {
assert(edges != null);
double horizontal = edges.left + edges.right;
double vertical = edges.top + edges.bottom;
double deflatedMinWidth = math.max(0.0, minWidth - horizontal);
double deflatedMinHeight = math.max(0.0, minHeight - vertical);
return new BoxConstraints(
minWidth: math.max(0.0, minWidth - horizontal),
maxWidth: maxWidth - horizontal,
minHeight: math.max(0.0, minHeight - vertical),
maxHeight: maxHeight - vertical
minWidth: deflatedMinWidth,
maxWidth: math.max(deflatedMinWidth, maxWidth - horizontal),
minHeight: deflatedMinHeight,
maxHeight: math.max(deflatedMinHeight, maxHeight - vertical)
);
}
/// Returns new box constraints that remove the minimum width and height requirements
BoxConstraints loosen() {
return new BoxConstraints(
minWidth: 0.0,
......@@ -91,7 +111,8 @@ class BoxConstraints extends Constraints {
);
}
BoxConstraints apply(BoxConstraints constraints) {
/// Returns new box constraints that respect the given constraints while being as close as possible to the original constraints
BoxConstraints enforce(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: clamp(min: constraints.minWidth, max: constraints.maxWidth, value: minWidth),
maxWidth: clamp(min: constraints.minWidth, max: constraints.maxWidth, value: maxWidth),
......@@ -100,81 +121,63 @@ class BoxConstraints extends Constraints {
);
}
BoxConstraints applyWidth(double width) {
/// Returns new box constraints with a tight width as close to the given width as possible while still respecting the original box constraints
BoxConstraints tightenWidth(double width) {
return new BoxConstraints(minWidth: math.max(math.min(maxWidth, width), minWidth),
maxWidth: math.max(math.min(maxWidth, width), minWidth),
minHeight: minHeight,
maxHeight: maxHeight);
}
BoxConstraints applyMinWidth(double newMinWidth) {
return new BoxConstraints(minWidth: math.max(minWidth, newMinWidth),
maxWidth: math.max(maxWidth, newMinWidth),
minHeight: minHeight,
maxHeight: maxHeight);
}
BoxConstraints applyMaxWidth(double newMaxWidth) {
return new BoxConstraints(minWidth: minWidth,
maxWidth: math.min(maxWidth, newMaxWidth),
minHeight: minHeight,
maxHeight: maxHeight);
}
BoxConstraints applyHeight(double height) {
/// Returns new box constraints with a tight height as close to the given height as possible while still respecting the original box constraints
BoxConstraints tightenHeight(double height) {
return new BoxConstraints(minWidth: minWidth,
maxWidth: maxWidth,
minHeight: math.max(math.min(maxHeight, height), minHeight),
maxHeight: math.max(math.min(maxHeight, height), minHeight));
}
BoxConstraints applyMinHeight(double newMinHeight) {
return new BoxConstraints(minWidth: minWidth,
maxWidth: maxWidth,
minHeight: math.max(minHeight, newMinHeight),
maxHeight: math.max(maxHeight, newMinHeight));
}
BoxConstraints applyMaxHeight(double newMaxHeight) {
return new BoxConstraints(minWidth: minWidth,
maxWidth: maxWidth,
minHeight: minHeight,
maxHeight: math.min(maxHeight, newMaxHeight));
}
/// Returns box constraints with the same width constraints but with unconstrainted height
BoxConstraints widthConstraints() => new BoxConstraints(minWidth: minWidth, maxWidth: maxWidth);
/// Returns box constraints with the same height constraints but with unconstrainted width
BoxConstraints heightConstraints() => new BoxConstraints(minHeight: minHeight, maxHeight: maxHeight);
final double minWidth;
final double maxWidth;
final double minHeight;
final double maxHeight;
/// Returns the width that both satisfies the constraints and is as close as possible to the given width
double constrainWidth([double width = double.INFINITY]) {
return clamp(min: minWidth, max: maxWidth, value: width);
}
/// Returns the height that both satisfies the constraints and is as close as possible to the given height
double constrainHeight([double height = double.INFINITY]) {
return clamp(min: minHeight, max: maxHeight, value: height);
}
/// Returns the size that both satisfies the constraints and is as close as possible to the given size
Size constrain(Size size) {
Size result = new Size(constrainWidth(size.width), constrainHeight(size.height));
if (size is _DebugSize)
result = new _DebugSize(result, size._owner, size._canBeUsedByParent);
return result;
}
/// The biggest size that satisifes the constraints
Size get biggest => new Size(constrainWidth(), constrainHeight());
Size get smallest => new Size(constrainWidth(0.0), constrainHeight(0.0));
bool get isInfinite => maxWidth >= double.INFINITY && maxHeight >= double.INFINITY;
/// The smallest size that satisfies the constraints
Size get smallest => new Size(constrainWidth(0.0), constrainHeight(0.0));
/// Whether there is exactly one width value that satisfies the constraints
bool get hasTightWidth => minWidth >= maxWidth;
/// Whether there is exactly one height value that satisfies the constraints
bool get hasTightHeight => minHeight >= maxHeight;
/// Whether there is exactly one size that satifies the constraints
bool get isTight => hasTightWidth && hasTightHeight;
bool contains(Size size) {
/// Whether the given size satisfies the constraints
bool isSatisfiedBy(Size size) {
return (minWidth <= size.width) && (size.width <= math.max(minWidth, maxWidth)) &&
(minHeight <= size.height) && (size.height <= math.max(minHeight, maxHeight));
}
......@@ -323,7 +326,7 @@ abstract class RenderBox extends RenderObject {
'See https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/lib/src/widgets/sizing.md#user-content-unbounded-constraints';
return !_size.isInfinite;
});
bool result = constraints.contains(_size);
bool result = constraints.isSatisfiedBy(_size);
if (!result)
print("${this.runtimeType} does not meet its constraints. Constraints: $constraints, size: $_size");
return result;
......
......@@ -85,7 +85,7 @@ class RenderImage extends RenderBox {
constraints = new BoxConstraints.tightFor(
width: _width,
height: _height
).apply(constraints);
).enforce(constraints);
if (constraints.isTight || _image == null)
return constraints.smallest;
......
......@@ -92,34 +92,34 @@ class RenderConstrainedBox extends RenderProxyBox {
double getMinIntrinsicWidth(BoxConstraints constraints) {
if (child != null)
return child.getMinIntrinsicWidth(_additionalConstraints.apply(constraints));
return _additionalConstraints.apply(constraints).constrainWidth(0.0);
return child.getMinIntrinsicWidth(_additionalConstraints.enforce(constraints));
return _additionalConstraints.enforce(constraints).constrainWidth(0.0);
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
if (child != null)
return child.getMaxIntrinsicWidth(_additionalConstraints.apply(constraints));
return _additionalConstraints.apply(constraints).constrainWidth(0.0);
return child.getMaxIntrinsicWidth(_additionalConstraints.enforce(constraints));
return _additionalConstraints.enforce(constraints).constrainWidth(0.0);
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
if (child != null)
return child.getMinIntrinsicHeight(_additionalConstraints.apply(constraints));
return _additionalConstraints.apply(constraints).constrainHeight(0.0);
return child.getMinIntrinsicHeight(_additionalConstraints.enforce(constraints));
return _additionalConstraints.enforce(constraints).constrainHeight(0.0);
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
if (child != null)
return child.getMaxIntrinsicHeight(_additionalConstraints.apply(constraints));
return _additionalConstraints.apply(constraints).constrainHeight(0.0);
return child.getMaxIntrinsicHeight(_additionalConstraints.enforce(constraints));
return _additionalConstraints.enforce(constraints).constrainHeight(0.0);
}
void performLayout() {
if (child != null) {
child.layout(_additionalConstraints.apply(constraints), parentUsesSize: true);
child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
size = child.size;
} else {
size = _additionalConstraints.apply(constraints).constrain(Size.zero);
size = _additionalConstraints.enforce(constraints).constrain(Size.zero);
}
}
......@@ -218,7 +218,7 @@ class RenderShrinkWrapWidth extends RenderProxyBox {
return constraints;
double width = child.getMaxIntrinsicWidth(constraints);
assert(width == constraints.constrainWidth(width));
return constraints.applyWidth(applyStep(width, _stepWidth));
return constraints.tightenWidth(applyStep(width, _stepWidth));
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
......@@ -250,7 +250,7 @@ class RenderShrinkWrapWidth extends RenderProxyBox {
if (child != null) {
BoxConstraints childConstraints = _getInnerConstraints(constraints);
if (_stepHeight != null)
childConstraints.applyHeight(getMaxIntrinsicHeight(childConstraints));
childConstraints.tightenHeight(getMaxIntrinsicHeight(childConstraints));
child.layout(childConstraints, parentUsesSize: true);
size = child.size;
} else {
......@@ -281,7 +281,7 @@ class RenderShrinkWrapHeight extends RenderProxyBox {
return constraints;
double height = child.getMaxIntrinsicHeight(constraints);
assert(height == constraints.constrainHeight(height));
return constraints.applyHeight(height);
return constraints.tightenHeight(height);
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
......
......@@ -155,10 +155,10 @@ class RenderStack extends RenderBox with ContainerRenderObjectMixin<RenderBox, S
BoxConstraints childConstraints = const BoxConstraints();
if (childData.left != null && childData.right != null)
childConstraints = childConstraints.applyWidth(size.width - childData.right - childData.left);
childConstraints = childConstraints.tightenWidth(size.width - childData.right - childData.left);
if (childData.top != null && childData.bottom != null)
childConstraints = childConstraints.applyHeight(size.height - childData.bottom - childData.top);
childConstraints = childConstraints.tightenHeight(size.height - childData.bottom - childData.top);
child.layout(childConstraints, parentUsesSize: true);
......
......@@ -246,9 +246,9 @@ class SizedBox extends OneChildRenderObjectWrapper {
BoxConstraints _additionalConstraints() {
BoxConstraints result = const BoxConstraints();
if (width != null)
result = result.applyWidth(width);
result = result.tightenWidth(width);
if (height != null)
result = result.applyHeight(height);
result = result.tightenHeight(height);
return result;
}
......@@ -430,7 +430,7 @@ class Container extends Component {
Widget current = child;
if (child == null && (width == null || height == null))
current = new ConstrainedBox(constraints: BoxConstraints.expand);
current = new ConstrainedBox(constraints: const BoxConstraints.expand());
EdgeDims effectivePadding = _paddingIncludingBorder;
if (effectivePadding != null)
......
......@@ -22,7 +22,7 @@ void main() {
test('Flex and padding', () {
RenderBox size = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints().applyHeight(100.0)
additionalConstraints: new BoxConstraints().tightenHeight(100.0)
);
RenderBox inner = new RenderDecoratedBox(
decoration: new BoxDecoration(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment