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 @@ ...@@ -5,79 +5,6 @@
import 'box.dart'; import 'box.dart';
import 'object.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, /// 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 /// without making the child available for hit testing, and without taking any
/// room in the parent. /// room in the parent.
......
...@@ -29,7 +29,6 @@ export 'package:flutter/gestures.dart' show ...@@ -29,7 +29,6 @@ export 'package:flutter/gestures.dart' show
/// for render objects that wish to mimic most, but not all, of the properties /// for render objects that wish to mimic most, but not all, of the properties
/// of their child. /// of their child.
class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> { class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderProxyBox([RenderBox child = null]) { RenderProxyBox([RenderBox child = null]) {
this.child = child; this.child = child;
} }
...@@ -247,128 +246,6 @@ class RenderConstrainedBox extends RenderProxyBox { ...@@ -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. /// Attempts to size the child to a specific aspect ratio.
/// ///
/// The render object first tries the largest width permited by the layout /// The render object first tries the largest width permited by the layout
......
...@@ -293,7 +293,7 @@ class Transform extends SingleChildRenderObjectWidget { ...@@ -293,7 +293,7 @@ class Transform extends SingleChildRenderObjectWidget {
/// The alignment of the origin, relative to the size of the box. /// 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. /// 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; final FractionalOffset alignment;
/// Whether to apply the translation when performing hit tests. /// Whether to apply the translation when performing hit tests.
...@@ -443,6 +443,16 @@ class Align extends SingleChildRenderObjectWidget { ...@@ -443,6 +443,16 @@ class Align extends SingleChildRenderObjectWidget {
..widthFactor = widthFactor ..widthFactor = widthFactor
..heightFactor = heightFactor; ..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. /// Centers its child within itself.
...@@ -610,10 +620,17 @@ class ConstrainedBox extends SingleChildRenderObjectWidget { ...@@ -610,10 +620,17 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
/// Sizes itself to a fraction of the total available space. /// Sizes itself to a fraction of the total available space.
/// ///
/// See [RenderFractionallySizedBox] for details. /// See [RenderFractionallySizedOverflowBox] for details.
class FractionallySizedBox extends SingleChildRenderObjectWidget { class FractionallySizedBox extends SingleChildRenderObjectWidget {
FractionallySizedBox({ Key key, this.width, this.height, Widget child }) FractionallySizedBox({
: super(key: key, child: child); 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. /// If non-null, the factor of the incoming width to use.
/// ///
...@@ -627,15 +644,28 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { ...@@ -627,15 +644,28 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
/// incoming height constraint multipled by this factor. /// incoming height constraint multipled by this factor.
final double height; 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 @override
RenderFractionallySizedBox createRenderObject(BuildContext context) => new RenderFractionallySizedBox( RenderFractionallySizedOverflowBox createRenderObject(BuildContext context) => new RenderFractionallySizedOverflowBox(
alignment: alignment,
widthFactor: width, widthFactor: width,
heightFactor: height heightFactor: height
); );
@override @override
void updateRenderObject(BuildContext context, RenderFractionallySizedBox renderObject) { void updateRenderObject(BuildContext context, RenderFractionallySizedOverflowBox renderObject) {
renderObject renderObject
..alignment = alignment
..widthFactor = width ..widthFactor = width
..heightFactor = height; ..heightFactor = height;
} }
...@@ -643,6 +673,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { ...@@ -643,6 +673,7 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
@override @override
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
description.add('alignment: $alignment');
if (width != null) if (width != null)
description.add('width: $width'); description.add('width: $width');
if (height != null) if (height != null)
...@@ -657,14 +688,25 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { ...@@ -657,14 +688,25 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
class OverflowBox extends SingleChildRenderObjectWidget { class OverflowBox extends SingleChildRenderObjectWidget {
OverflowBox({ OverflowBox({
Key key, Key key,
this.alignment: const FractionalOffset(0.5, 0.5),
this.minWidth, this.minWidth,
this.maxWidth, this.maxWidth,
this.minHeight, this.minHeight,
this.maxHeight, this.maxHeight,
this.alignment: const FractionalOffset(0.5, 0.5),
Widget child Widget child
}) : super(key: key, child: 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 /// The minimum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead. /// default) to use the constraint from the parent instead.
final double minWidth; final double minWidth;
...@@ -681,39 +723,29 @@ class OverflowBox extends SingleChildRenderObjectWidget { ...@@ -681,39 +723,29 @@ class OverflowBox extends SingleChildRenderObjectWidget {
/// default) to use the constraint from the parent instead. /// default) to use the constraint from the parent instead.
final double maxHeight; 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 @override
RenderOverflowBox createRenderObject(BuildContext context) => new RenderOverflowBox( RenderConstrainedOverflowBox createRenderObject(BuildContext context) => new RenderConstrainedOverflowBox(
alignment: alignment,
minWidth: minWidth, minWidth: minWidth,
maxWidth: maxWidth, maxWidth: maxWidth,
minHeight: minHeight, minHeight: minHeight,
maxHeight: maxHeight, maxHeight: maxHeight
alignment: alignment
); );
@override @override
void updateRenderObject(BuildContext context, RenderOverflowBox renderObject) { void updateRenderObject(BuildContext context, RenderConstrainedOverflowBox renderObject) {
renderObject renderObject
..alignment = alignment
..minWidth = minWidth ..minWidth = minWidth
..maxWidth = maxWidth ..maxWidth = maxWidth
..minHeight = minHeight ..minHeight = minHeight
..maxHeight = maxHeight ..maxHeight = maxHeight;
..alignment = alignment;
} }
@override @override
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
description.add('alignment: $alignment');
if (minWidth != null) if (minWidth != null)
description.add('minWidth: $minWidth'); description.add('minWidth: $minWidth');
if (maxWidth != null) if (maxWidth != null)
...@@ -726,17 +758,49 @@ class OverflowBox extends SingleChildRenderObjectWidget { ...@@ -726,17 +758,49 @@ class OverflowBox extends SingleChildRenderObjectWidget {
} }
class SizedOverflowBox extends SingleChildRenderObjectWidget { class SizedOverflowBox extends SingleChildRenderObjectWidget {
SizedOverflowBox({ Key key, this.size, Widget child }) SizedOverflowBox({
: super(key: key, child: child); 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; final Size size;
@override @override
RenderSizedOverflowBox createRenderObject(BuildContext context) => new RenderSizedOverflowBox(requestedSize: size); RenderSizedOverflowBox createRenderObject(BuildContext context) {
return new RenderSizedOverflowBox(
alignment: alignment,
requestedSize: size
);
}
@override @override
void updateRenderObject(BuildContext context, RenderSizedOverflowBox renderObject) { 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() { ...@@ -13,7 +13,7 @@ void main() {
root = new RenderPositionedBox( root = new RenderPositionedBox(
child: new RenderConstrainedBox( child: new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tight(const Size(200.0, 200.0)), additionalConstraints: new BoxConstraints.tight(const Size(200.0, 200.0)),
child: test = new RenderFractionallySizedBox( child: test = new RenderFractionallySizedOverflowBox(
widthFactor: 2.0, widthFactor: 2.0,
heightFactor: 0.5, heightFactor: 0.5,
child: leaf = new RenderConstrainedBox( child: leaf = new RenderConstrainedBox(
......
...@@ -28,7 +28,7 @@ void main() { ...@@ -28,7 +28,7 @@ void main() {
root = new RenderPositionedBox( root = new RenderPositionedBox(
child: new RenderCustomPaint( child: new RenderCustomPaint(
child: child = new RenderOverflowBox( child: child = new RenderConstrainedOverflowBox(
child: text = new RenderParagraph(new TextSpan(text: 'Hello World')), child: text = new RenderParagraph(new TextSpan(text: 'Hello World')),
maxHeight: height1 / 2.0, maxHeight: height1 / 2.0,
alignment: const FractionalOffset(0.0, 0.0) alignment: const FractionalOffset(0.0, 0.0)
......
...@@ -42,6 +42,7 @@ void main() { ...@@ -42,6 +42,7 @@ void main() {
maxHeight: 4.0 maxHeight: 4.0
).debugFillDescription(description); ).debugFillDescription(description);
expect(description, [ expect(description, [
'alignment: FractionalOffset(0.5, 0.5)',
'minWidth: 1.0', 'minWidth: 1.0',
'maxWidth: 2.0', 'maxWidth: 2.0',
'minHeight: 3.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