Commit cae847dc authored by Ian Hickson's avatar Ian Hickson

Merge pull request #2835 from Hixie/RenderFractionallySizedBox-constraints

Rationalise the overflow render boxes
parents e074af80 34cc0c47
......@@ -5,79 +5,6 @@
import 'box.dart';
import 'object.dart';
export 'package:flutter/src/painting/box_painter.dart';
/// A render box that's a specific size but passes its original constraints through to its child, which will probably overflow
class RenderSizedOverflowBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderSizedOverflowBox({
RenderBox child,
Size requestedSize
}) : _requestedSize = requestedSize {
assert(requestedSize != null);
this.child = child;
}
/// The size this render box should attempt to be.
Size get requestedSize => _requestedSize;
Size _requestedSize;
void set requestedSize (Size value) {
assert(value != null);
if (_requestedSize == value)
return;
_requestedSize = value;
markNeedsLayout();
}
@override
double getMinIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
return constraints.constrainWidth(_requestedSize.width);
}
@override
double getMaxIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
return constraints.constrainWidth(_requestedSize.width);
}
@override
double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
return constraints.constrainHeight(_requestedSize.height);
}
@override
double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
return constraints.constrainHeight(_requestedSize.height);
}
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
if (child != null)
return child.getDistanceToActualBaseline(baseline);
return super.computeDistanceToActualBaseline(baseline);
}
@override
void performLayout() {
size = constraints.constrain(_requestedSize);
if (child != null)
child.layout(constraints);
}
@override
bool hitTestChildren(HitTestResult result, { Point position }) {
return child?.hitTest(result, position: position) ?? false;
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChild(child, offset);
}
}
/// Lays the child out as if it was in the tree, but without painting anything,
/// without making the child available for hit testing, and without taking any
/// room in the parent.
......
......@@ -29,7 +29,6 @@ export 'package:flutter/gestures.dart' show
/// for render objects that wish to mimic most, but not all, of the properties
/// of their child.
class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderProxyBox([RenderBox child = null]) {
this.child = child;
}
......@@ -247,128 +246,6 @@ class RenderConstrainedBox extends RenderProxyBox {
}
}
/// Sizes its child to a fraction of the total available space.
///
/// For both its width and height, this render object imposes a tight
/// constraint on its child that is a multiple (typically less than 1.0) of the
/// maximum constraint it received from its parent on that axis. If the factor
/// for a given axis is null, then the constraints from the parent are just
/// passed through instead.
///
/// It then tries to size itself to the size of its child.
class RenderFractionallySizedBox extends RenderProxyBox {
RenderFractionallySizedBox({
RenderBox child,
double widthFactor,
double heightFactor
}) : _widthFactor = widthFactor, _heightFactor = heightFactor, super(child) {
assert(_widthFactor == null || _widthFactor >= 0.0);
assert(_heightFactor == null || _heightFactor >= 0.0);
}
/// If non-null, the factor of the incoming width to use.
///
/// If non-null, the child is given a tight width constraint that is the max
/// incoming width constraint multipled by this factor. If null, the child is
/// given the incoming width constraings.
double get widthFactor => _widthFactor;
double _widthFactor;
void set widthFactor (double value) {
assert(value == null || value >= 0.0);
if (_widthFactor == value)
return;
_widthFactor = value;
markNeedsLayout();
}
/// If non-null, the factor of the incoming height to use.
///
/// If non-null, the child is given a tight height constraint that is the max
/// incoming width constraint multipled by this factor. If null, the child is
/// given the incoming width constraings.
double get heightFactor => _heightFactor;
double _heightFactor;
void set heightFactor (double value) {
assert(value == null || value >= 0.0);
if (_heightFactor == value)
return;
_heightFactor = value;
markNeedsLayout();
}
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
double minWidth = constraints.minWidth;
double maxWidth = constraints.maxWidth;
if (_widthFactor != null) {
double width = maxWidth * _widthFactor;
minWidth = width;
maxWidth = width;
}
double minHeight = constraints.minHeight;
double maxHeight = constraints.maxHeight;
if (_heightFactor != null) {
double height = maxHeight * _heightFactor;
minHeight = height;
maxHeight = height;
}
return new BoxConstraints(
minWidth: minWidth,
maxWidth: maxWidth,
minHeight: minHeight,
maxHeight: maxHeight
);
}
@override
double getMinIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return constraints.constrainWidth(child.getMinIntrinsicWidth(_getInnerConstraints(constraints)));
return constraints.constrainWidth(_getInnerConstraints(constraints).constrainWidth(0.0));
}
@override
double getMaxIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return constraints.constrainWidth(child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)));
return constraints.constrainWidth(_getInnerConstraints(constraints).constrainWidth(0.0));
}
@override
double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return constraints.constrainHeight(child.getMinIntrinsicHeight(_getInnerConstraints(constraints)));
return constraints.constrainHeight(_getInnerConstraints(constraints).constrainHeight(0.0));
}
@override
double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsNormalized);
if (child != null)
return constraints.constrainHeight(child.getMaxIntrinsicHeight(_getInnerConstraints(constraints)));
return constraints.constrainHeight(_getInnerConstraints(constraints).constrainHeight(0.0));
}
@override
void performLayout() {
if (child != null) {
child.layout(_getInnerConstraints(constraints), parentUsesSize: true);
size = constraints.constrain(child.size);
} else {
size = constraints.constrain(_getInnerConstraints(constraints).constrain(Size.zero));
}
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('widthFactor: ${_widthFactor ?? "pass-through"}');
description.add('heightFactor: ${_heightFactor ?? "pass-through"}');
}
}
/// Attempts to size the child to a specific aspect ratio.
///
/// The render object first tries the largest width permited by the layout
......
......@@ -293,7 +293,7 @@ class Transform extends SingleChildRenderObjectWidget {
/// The alignment of the origin, relative to the size of the box.
///
/// This is equivalent to setting an origin based on the size of the box.
/// If it is specificed at the same time as an offset, both are applied.
/// If it is specified at the same time as an offset, both are applied.
final FractionalOffset alignment;
/// Whether to apply the translation when performing hit tests.
......@@ -443,6 +443,16 @@ class Align extends SingleChildRenderObjectWidget {
..widthFactor = widthFactor
..heightFactor = heightFactor;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('alignment: $alignment');
if (widthFactor != null)
description.add('widthFactor: $widthFactor');
if (heightFactor != null)
description.add('heightFactor: $heightFactor');
}
}
/// Centers its child within itself.
......@@ -610,10 +620,17 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// Sizes itself to a fraction of the total available space.
///
/// See [RenderFractionallySizedBox] for details.
/// See [RenderFractionallySizedOverflowBox] for details.
class FractionallySizedBox extends SingleChildRenderObjectWidget {
FractionallySizedBox({ Key key, this.width, this.height, Widget child })
: super(key: key, child: child);
FractionallySizedBox({
Key key,
this.alignment: const FractionalOffset(0.5, 0.5),
this.width,
this.height,
Widget child
}) : super(key: key, child: child) {
assert(alignment != null && alignment.dx != null && alignment.dy != null);
}
/// If non-null, the factor of the incoming width to use.
///
......@@ -627,15 +644,28 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
/// incoming height constraint multipled by this factor.
final double height;
/// How to align the child.
///
/// The x and y values of the alignment control the horizontal and vertical
/// alignment, respectively. An x value of 0.0 means that the left edge of
/// the child is aligned with the left edge of the parent whereas an x value
/// of 1.0 means that the right edge of the child is aligned with the right
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
/// For example, a value of 0.5 means that the center of the child is aligned
/// with the center of the parent.
final FractionalOffset alignment;
@override
RenderFractionallySizedBox createRenderObject(BuildContext context) => new RenderFractionallySizedBox(
RenderFractionallySizedOverflowBox createRenderObject(BuildContext context) => new RenderFractionallySizedOverflowBox(
alignment: alignment,
widthFactor: width,
heightFactor: height
);
@override
void updateRenderObject(BuildContext context, RenderFractionallySizedBox renderObject) {
void updateRenderObject(BuildContext context, RenderFractionallySizedOverflowBox renderObject) {
renderObject
..alignment = alignment
..widthFactor = width
..heightFactor = height;
}
......@@ -643,6 +673,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('alignment: $alignment');
if (width != null)
description.add('width: $width');
if (height != null)
......@@ -657,14 +688,25 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
class OverflowBox extends SingleChildRenderObjectWidget {
OverflowBox({
Key key,
this.alignment: const FractionalOffset(0.5, 0.5),
this.minWidth,
this.maxWidth,
this.minHeight,
this.maxHeight,
this.alignment: const FractionalOffset(0.5, 0.5),
Widget child
}) : super(key: key, child: child);
/// How to align the child.
///
/// The x and y values of the alignment control the horizontal and vertical
/// alignment, respectively. An x value of 0.0 means that the left edge of
/// the child is aligned with the left edge of the parent whereas an x value
/// of 1.0 means that the right edge of the child is aligned with the right
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
/// For example, a value of 0.5 means that the center of the child is aligned
/// with the center of the parent.
final FractionalOffset alignment;
/// The minimum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
final double minWidth;
......@@ -681,39 +723,29 @@ class OverflowBox extends SingleChildRenderObjectWidget {
/// default) to use the constraint from the parent instead.
final double maxHeight;
/// How to align the child.
///
/// The x and y values of the alignment control the horizontal and vertical
/// alignment, respectively. An x value of 0.0 means that the left edge of
/// the child is aligned with the left edge of the parent whereas an x value
/// of 1.0 means that the right edge of the child is aligned with the right
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
/// For example, a value of 0.5 means that the center of the child is aligned
/// with the center of the parent.
final FractionalOffset alignment;
@override
RenderOverflowBox createRenderObject(BuildContext context) => new RenderOverflowBox(
RenderConstrainedOverflowBox createRenderObject(BuildContext context) => new RenderConstrainedOverflowBox(
alignment: alignment,
minWidth: minWidth,
maxWidth: maxWidth,
minHeight: minHeight,
maxHeight: maxHeight,
alignment: alignment
maxHeight: maxHeight
);
@override
void updateRenderObject(BuildContext context, RenderOverflowBox renderObject) {
void updateRenderObject(BuildContext context, RenderConstrainedOverflowBox renderObject) {
renderObject
..alignment = alignment
..minWidth = minWidth
..maxWidth = maxWidth
..minHeight = minHeight
..maxHeight = maxHeight
..alignment = alignment;
..maxHeight = maxHeight;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('alignment: $alignment');
if (minWidth != null)
description.add('minWidth: $minWidth');
if (maxWidth != null)
......@@ -726,17 +758,49 @@ class OverflowBox extends SingleChildRenderObjectWidget {
}
class SizedOverflowBox extends SingleChildRenderObjectWidget {
SizedOverflowBox({ Key key, this.size, Widget child })
: super(key: key, child: child);
SizedOverflowBox({
Key key,
this.alignment: const FractionalOffset(0.5, 0.5),
this.size,
Widget child
}) : super(key: key, child: child) {
assert(alignment != null && alignment.dx != null && alignment.dy != null);
}
/// How to align the child.
///
/// The x and y values of the alignment control the horizontal and vertical
/// alignment, respectively. An x value of 0.0 means that the left edge of
/// the child is aligned with the left edge of the parent whereas an x value
/// of 1.0 means that the right edge of the child is aligned with the right
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
/// For example, a value of 0.5 means that the center of the child is aligned
/// with the center of the parent.
final FractionalOffset alignment;
final Size size;
@override
RenderSizedOverflowBox createRenderObject(BuildContext context) => new RenderSizedOverflowBox(requestedSize: size);
RenderSizedOverflowBox createRenderObject(BuildContext context) {
return new RenderSizedOverflowBox(
alignment: alignment,
requestedSize: size
);
}
@override
void updateRenderObject(BuildContext context, RenderSizedOverflowBox renderObject) {
renderObject.requestedSize = size;
renderObject
..alignment = alignment
..requestedSize = size;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('alignment: $alignment');
if (size != null)
description.add('size: $size');
}
}
......
......@@ -13,7 +13,7 @@ void main() {
root = new RenderPositionedBox(
child: new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tight(const Size(200.0, 200.0)),
child: test = new RenderFractionallySizedBox(
child: test = new RenderFractionallySizedOverflowBox(
widthFactor: 2.0,
heightFactor: 0.5,
child: leaf = new RenderConstrainedBox(
......
......@@ -28,7 +28,7 @@ void main() {
root = new RenderPositionedBox(
child: new RenderCustomPaint(
child: child = new RenderOverflowBox(
child: child = new RenderConstrainedOverflowBox(
child: text = new RenderParagraph(new TextSpan(text: 'Hello World')),
maxHeight: height1 / 2.0,
alignment: const FractionalOffset(0.0, 0.0)
......
......@@ -42,6 +42,7 @@ void main() {
maxHeight: 4.0
).debugFillDescription(description);
expect(description, [
'alignment: FractionalOffset(0.5, 0.5)',
'minWidth: 1.0',
'maxWidth: 2.0',
'minHeight: 3.0',
......
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