Commit 15fb5c4c authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clean up some RenderObject layer stuff (#6883)

More idiomatic use of constraints in performResize.

Trivial fixes to comments.

Make ProxyBox not use BoxParentData since it ignores the field.

Make applyPaintTransform more helpful if you use a different ParentData
subclass than RenderBox expects.

Make debugAssertIsValid actually fulfill its contract in RenderObject as
documented.

Add a childBefore for symmetry (we already had childAfter).

Fix the way we dump the child list when there's no children in a
multichild render object.

More asserts in the rendering test library.
parent a8fe83ae
...@@ -1631,7 +1631,7 @@ abstract class RenderBox extends RenderObject { ...@@ -1631,7 +1631,7 @@ abstract class RenderBox extends RenderObject {
@override @override
void performResize() { void performResize() {
// default behavior for subclasses that have sizedByParent = true // default behavior for subclasses that have sizedByParent = true
size = constraints.constrain(Size.zero); size = constraints.smallest;
assert(!size.isInfinite); assert(!size.isInfinite);
} }
...@@ -1657,7 +1657,7 @@ abstract class RenderBox extends RenderObject { ...@@ -1657,7 +1657,7 @@ abstract class RenderBox extends RenderObject {
/// given hit test result. /// given hit test result.
/// ///
/// The caller is responsible for transforming [position] into the local /// The caller is responsible for transforming [position] into the local
/// coordinate space of the callee. The callee is responsible for checking /// coordinate space of the callee. The callee is responsible for checking
/// whether the given position is within its bounds. /// whether the given position is within its bounds.
/// ///
/// Hit testing requires layout to be up-to-date but does not require painting /// Hit testing requires layout to be up-to-date but does not require painting
...@@ -1712,7 +1712,7 @@ abstract class RenderBox extends RenderObject { ...@@ -1712,7 +1712,7 @@ abstract class RenderBox extends RenderObject {
/// Override this method to check whether any children are located at the /// Override this method to check whether any children are located at the
/// given position. /// given position.
/// ///
/// Typically children should be hit tested in reverse paint order so that /// Typically children should be hit-tested in reverse paint order so that
/// hit tests at locations where children overlap hit the child that is /// hit tests at locations where children overlap hit the child that is
/// visually "on top" (i.e., paints later). /// visually "on top" (i.e., paints later).
/// ///
...@@ -1733,9 +1733,28 @@ abstract class RenderBox extends RenderObject { ...@@ -1733,9 +1733,28 @@ abstract class RenderBox extends RenderObject {
/// child's [parentData] in the [BoxParentData.offset] field. /// child's [parentData] in the [BoxParentData.offset] field.
@override @override
void applyPaintTransform(RenderObject child, Matrix4 transform) { void applyPaintTransform(RenderObject child, Matrix4 transform) {
assert(child != null);
assert(child.parent == this); assert(child.parent == this);
BoxParentData childParentData = child.parentData; assert(() {
Offset offset = childParentData.offset; if (child.parentData is! BoxParentData) {
throw new FlutterError(
'$runtimeType does not implement applyPaintTransform.\n'
'The following $runtimeType object:\n'
' ${this.toStringShallow()}\n'
'...did not use a BoxParentData class for the parentData field of the following child:\n'
' ${child.toStringShallow()}\n'
'The $runtimeType class inherits from RenderBox. '
'The default applyPaintTransform implementation provided by RenderBox assumes that the '
'children all use BoxParentData objects for their parentData field. '
'Since $runtimeType does not in fact use that ParentData class for its children, it must '
'provide an implementation of applyPaintTransform that supports the specific ParentData '
'subclass used by its children (which apparently is ${child.parentData.runtimeType}).'
);
}
return true;
});
final BoxParentData childParentData = child.parentData;
final Offset offset = childParentData.offset;
transform.translate(offset.dx, offset.dy); transform.translate(offset.dx, offset.dy);
} }
......
...@@ -515,7 +515,10 @@ abstract class Constraints { ...@@ -515,7 +515,10 @@ abstract class Constraints {
bool debugAssertIsValid({ bool debugAssertIsValid({
bool isAppliedConstraint: false, bool isAppliedConstraint: false,
InformationCollector informationCollector InformationCollector informationCollector
}); }) {
assert(isNormalized);
return isNormalized;
}
} }
/// Signature for a function that is called for each [RenderObject]. /// Signature for a function that is called for each [RenderObject].
...@@ -1296,7 +1299,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1296,7 +1299,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
/// Calls visitor for each immediate child of this render object. /// Calls visitor for each immediate child of this render object.
/// ///
/// Override in subclasses with children and call the visitor for each child /// Override in subclasses with children and call the visitor for each child.
void visitChildren(RenderObjectVisitor visitor) { } void visitChildren(RenderObjectVisitor visitor) { }
/// The object responsible for creating this render object. /// The object responsible for creating this render object.
...@@ -2606,6 +2609,9 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent ...@@ -2606,6 +2609,9 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
} }
} }
/// Insert child into this render object's child list after the given child. /// Insert child into this render object's child list after the given child.
///
/// If `after` is null, then this inserts the child at the start of the list,
/// and the child becomes the new [firstChild].
void insert(ChildType child, { ChildType after }) { void insert(ChildType child, { ChildType after }) {
assert(child != this); assert(child != this);
assert(after != this); assert(after != this);
...@@ -2744,30 +2750,41 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent ...@@ -2744,30 +2750,41 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
/// The last child in the child list. /// The last child in the child list.
ChildType get lastChild => _lastChild; ChildType get lastChild => _lastChild;
/// The previous child before the given child in the child list.
ChildType childBefore(ChildType child) {
assert(child != null);
assert(child.parent == this);
final ParentDataType childParentData = child.parentData;
return childParentData.previousSibling;
}
/// The next child after the given child in the child list. /// The next child after the given child in the child list.
ChildType childAfter(ChildType child) { ChildType childAfter(ChildType child) {
assert(child != null);
assert(child.parent == this);
final ParentDataType childParentData = child.parentData; final ParentDataType childParentData = child.parentData;
return childParentData.nextSibling; return childParentData.nextSibling;
} }
@override @override
String debugDescribeChildren(String prefix) { String debugDescribeChildren(String prefix) {
String result = '$prefix \u2502\n'; if (firstChild != null) {
if (_firstChild != null) { String result = '$prefix \u2502\n';
ChildType child = _firstChild; ChildType child = firstChild;
int count = 1; int count = 1;
while (child != _lastChild) { while (child != lastChild) {
result += '${child.toStringDeep("$prefix \u251C\u2500child $count: ", "$prefix \u2502")}'; result += '${child.toStringDeep("$prefix \u251C\u2500child $count: ", "$prefix \u2502")}';
count += 1; count += 1;
final ParentDataType childParentData = child.parentData; final ParentDataType childParentData = child.parentData;
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
if (child != null) { if (child != null) {
assert(child == _lastChild); assert(child == lastChild);
result += '${child.toStringDeep("$prefix \u2514\u2500child $count: ", "$prefix ")}'; result += '${child.toStringDeep("$prefix \u2514\u2500child $count: ", "$prefix ")}';
} }
return result;
} }
return result; return '';
} }
} }
......
...@@ -51,6 +51,14 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox ...@@ -51,6 +51,14 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
/// impractical (e.g. because you want to mix in other classes as well). /// impractical (e.g. because you want to mix in other classes as well).
// TODO(ianh): Remove this class once https://github.com/dart-lang/sdk/issues/15101 is fixed // TODO(ianh): Remove this class once https://github.com/dart-lang/sdk/issues/15101 is fixed
abstract class RenderProxyBoxMixin implements RenderBox, RenderObjectWithChildMixin<RenderBox> { abstract class RenderProxyBoxMixin implements RenderBox, RenderObjectWithChildMixin<RenderBox> {
@override
void setupParentData(RenderObject child) {
// We don't actually use the offset argument in BoxParentData, so let's
// avoid allocating it at all.
if (child.parentData is! ParentData)
child.parentData = new ParentData();
}
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
if (child != null) if (child != null)
...@@ -101,6 +109,9 @@ abstract class RenderProxyBoxMixin implements RenderBox, RenderObjectWithChildMi ...@@ -101,6 +109,9 @@ abstract class RenderProxyBoxMixin implements RenderBox, RenderObjectWithChildMi
return child?.hitTest(result, position: position) ?? false; return child?.hitTest(result, position: position) ?? false;
} }
@override
void applyPaintTransform(RenderObject child, Matrix4 transform) { }
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (child != null) if (child != null)
......
...@@ -15,8 +15,10 @@ void main() { ...@@ -15,8 +15,10 @@ void main() {
backgroundColor: const Color(0xFF00FF00), backgroundColor: const Color(0xFF00FF00),
gradient: new RadialGradient( gradient: new RadialGradient(
center: FractionalOffset.topLeft, radius: 1.8, center: FractionalOffset.topLeft, radius: 1.8,
colors: <Color>[Colors.yellow[500], Colors.blue[500]]), colors: <Color>[Colors.yellow[500], Colors.blue[500]],
boxShadow: kElevationToShadow[3]) ),
boxShadow: kElevationToShadow[3],
),
); );
layout(root); layout(root);
expect(root.size.width, equals(800.0)); expect(root.size.width, equals(800.0));
...@@ -25,28 +27,28 @@ void main() { ...@@ -25,28 +27,28 @@ void main() {
test('Flex and padding', () { test('Flex and padding', () {
RenderBox size = new RenderConstrainedBox( RenderBox size = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints().tighten(height: 100.0) additionalConstraints: new BoxConstraints().tighten(height: 100.0),
); );
RenderBox inner = new RenderDecoratedBox( RenderBox inner = new RenderDecoratedBox(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: const Color(0xFF00FF00) backgroundColor: const Color(0xFF00FF00),
), ),
child: size child: size,
); );
RenderBox padding = new RenderPadding( RenderBox padding = new RenderPadding(
padding: new EdgeInsets.all(50.0), padding: new EdgeInsets.all(50.0),
child: inner child: inner,
); );
RenderBox flex = new RenderFlex( RenderBox flex = new RenderFlex(
children: <RenderBox>[padding], children: <RenderBox>[padding],
direction: Axis.vertical, direction: Axis.vertical,
crossAxisAlignment: CrossAxisAlignment.stretch crossAxisAlignment: CrossAxisAlignment.stretch,
); );
RenderBox outer = new RenderDecoratedBox( RenderBox outer = new RenderDecoratedBox(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: const Color(0xFF0000FF) backgroundColor: const Color(0xFF0000FF)
), ),
child: flex child: flex,
); );
layout(outer); layout(outer);
...@@ -65,13 +67,15 @@ void main() { ...@@ -65,13 +67,15 @@ void main() {
test("should not have a 0 sized colored Box", () { test("should not have a 0 sized colored Box", () {
RenderBox coloredBox = new RenderDecoratedBox( RenderBox coloredBox = new RenderDecoratedBox(
decoration: new BoxDecoration() decoration: new BoxDecoration(),
);
RenderBox paddingBox = new RenderPadding(
padding: const EdgeInsets.all(10.0),
child: coloredBox,
); );
RenderBox paddingBox = new RenderPadding(padding: const EdgeInsets.all(10.0),
child: coloredBox);
RenderBox root = new RenderDecoratedBox( RenderBox root = new RenderDecoratedBox(
decoration: new BoxDecoration(), decoration: new BoxDecoration(),
child: paddingBox child: paddingBox,
); );
layout(root); layout(root);
expect(coloredBox.size.width, equals(780.0)); expect(coloredBox.size.width, equals(780.0));
...@@ -80,22 +84,23 @@ void main() { ...@@ -80,22 +84,23 @@ void main() {
test("reparenting should clear position", () { test("reparenting should clear position", () {
RenderDecoratedBox coloredBox = new RenderDecoratedBox( RenderDecoratedBox coloredBox = new RenderDecoratedBox(
decoration: new BoxDecoration()); decoration: new BoxDecoration(),
RenderPadding paddedBox = new RenderPadding( );
child: coloredBox, padding: const EdgeInsets.all(10.0));
RenderPadding paddedBox = new RenderPadding(
child: coloredBox,
padding: const EdgeInsets.all(10.0),
);
layout(paddedBox); layout(paddedBox);
BoxParentData parentData = coloredBox.parentData; BoxParentData parentData = coloredBox.parentData;
expect(parentData.offset.dx, isNot(equals(0.0))); expect(parentData.offset.dx, isNot(equals(0.0)));
paddedBox.child = null; paddedBox.child = null;
RenderConstrainedBox constraintedBox = new RenderConstrainedBox(
child: coloredBox, additionalConstraints: const BoxConstraints());
RenderConstrainedBox constraintedBox = new RenderConstrainedBox(
child: coloredBox,
additionalConstraints: const BoxConstraints(),
);
layout(constraintedBox); layout(constraintedBox);
expect(coloredBox.parentData?.runtimeType, ParentData);
parentData = coloredBox.parentData;
expect(parentData.offset.dx, equals(0.0));
}); });
} }
...@@ -70,6 +70,8 @@ void layout(RenderBox box, { ...@@ -70,6 +70,8 @@ void layout(RenderBox box, {
void pumpFrame({ EnginePhase phase: EnginePhase.layout }) { void pumpFrame({ EnginePhase phase: EnginePhase.layout }) {
assert(renderer != null); assert(renderer != null);
assert(renderer.renderView != null);
assert(renderer.renderView.child != null); // call layout() first!
renderer.phase = phase; renderer.phase = phase;
renderer.beginFrame(); renderer.beginFrame();
} }
......
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