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.
This commit is contained in:
@@ -1631,7 +1631,7 @@ abstract class RenderBox extends RenderObject {
|
||||
@override
|
||||
void performResize() {
|
||||
// default behavior for subclasses that have sizedByParent = true
|
||||
size = constraints.constrain(Size.zero);
|
||||
size = constraints.smallest;
|
||||
assert(!size.isInfinite);
|
||||
}
|
||||
|
||||
@@ -1657,7 +1657,7 @@ abstract class RenderBox extends RenderObject {
|
||||
/// given hit test result.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Hit testing requires layout to be up-to-date but does not require painting
|
||||
@@ -1712,7 +1712,7 @@ abstract class RenderBox extends RenderObject {
|
||||
/// Override this method to check whether any children are located at the
|
||||
/// 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
|
||||
/// visually "on top" (i.e., paints later).
|
||||
///
|
||||
@@ -1733,9 +1733,28 @@ abstract class RenderBox extends RenderObject {
|
||||
/// child's [parentData] in the [BoxParentData.offset] field.
|
||||
@override
|
||||
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
||||
assert(child != null);
|
||||
assert(child.parent == this);
|
||||
BoxParentData childParentData = child.parentData;
|
||||
Offset offset = childParentData.offset;
|
||||
assert(() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +515,10 @@ abstract class Constraints {
|
||||
bool debugAssertIsValid({
|
||||
bool isAppliedConstraint: false,
|
||||
InformationCollector informationCollector
|
||||
});
|
||||
}) {
|
||||
assert(isNormalized);
|
||||
return isNormalized;
|
||||
}
|
||||
}
|
||||
|
||||
/// Signature for a function that is called for each [RenderObject].
|
||||
@@ -1296,7 +1299,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
|
||||
/// 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) { }
|
||||
|
||||
/// The object responsible for creating this render object.
|
||||
@@ -2606,6 +2609,9 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
|
||||
}
|
||||
}
|
||||
/// 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 }) {
|
||||
assert(child != this);
|
||||
assert(after != this);
|
||||
@@ -2744,30 +2750,41 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
|
||||
/// The last child in the child list.
|
||||
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.
|
||||
ChildType childAfter(ChildType child) {
|
||||
assert(child != null);
|
||||
assert(child.parent == this);
|
||||
final ParentDataType childParentData = child.parentData;
|
||||
return childParentData.nextSibling;
|
||||
}
|
||||
|
||||
@override
|
||||
String debugDescribeChildren(String prefix) {
|
||||
String result = '$prefix \u2502\n';
|
||||
if (_firstChild != null) {
|
||||
ChildType child = _firstChild;
|
||||
if (firstChild != null) {
|
||||
String result = '$prefix \u2502\n';
|
||||
ChildType child = firstChild;
|
||||
int count = 1;
|
||||
while (child != _lastChild) {
|
||||
while (child != lastChild) {
|
||||
result += '${child.toStringDeep("$prefix \u251C\u2500child $count: ", "$prefix \u2502")}';
|
||||
count += 1;
|
||||
final ParentDataType childParentData = child.parentData;
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
if (child != null) {
|
||||
assert(child == _lastChild);
|
||||
assert(child == lastChild);
|
||||
result += '${child.toStringDeep("$prefix \u2514\u2500child $count: ", "$prefix ")}';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,14 @@ class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
/// 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
|
||||
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
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
if (child != null)
|
||||
@@ -101,6 +109,9 @@ abstract class RenderProxyBoxMixin implements RenderBox, RenderObjectWithChildMi
|
||||
return child?.hitTest(result, position: position) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
void applyPaintTransform(RenderObject child, Matrix4 transform) { }
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child != null)
|
||||
|
||||
@@ -15,8 +15,10 @@ void main() {
|
||||
backgroundColor: const Color(0xFF00FF00),
|
||||
gradient: new RadialGradient(
|
||||
center: FractionalOffset.topLeft, radius: 1.8,
|
||||
colors: <Color>[Colors.yellow[500], Colors.blue[500]]),
|
||||
boxShadow: kElevationToShadow[3])
|
||||
colors: <Color>[Colors.yellow[500], Colors.blue[500]],
|
||||
),
|
||||
boxShadow: kElevationToShadow[3],
|
||||
),
|
||||
);
|
||||
layout(root);
|
||||
expect(root.size.width, equals(800.0));
|
||||
@@ -25,28 +27,28 @@ void main() {
|
||||
|
||||
test('Flex and padding', () {
|
||||
RenderBox size = new RenderConstrainedBox(
|
||||
additionalConstraints: new BoxConstraints().tighten(height: 100.0)
|
||||
additionalConstraints: new BoxConstraints().tighten(height: 100.0),
|
||||
);
|
||||
RenderBox inner = new RenderDecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: const Color(0xFF00FF00)
|
||||
backgroundColor: const Color(0xFF00FF00),
|
||||
),
|
||||
child: size
|
||||
child: size,
|
||||
);
|
||||
RenderBox padding = new RenderPadding(
|
||||
padding: new EdgeInsets.all(50.0),
|
||||
child: inner
|
||||
child: inner,
|
||||
);
|
||||
RenderBox flex = new RenderFlex(
|
||||
children: <RenderBox>[padding],
|
||||
direction: Axis.vertical,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
);
|
||||
RenderBox outer = new RenderDecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: const Color(0xFF0000FF)
|
||||
),
|
||||
child: flex
|
||||
child: flex,
|
||||
);
|
||||
|
||||
layout(outer);
|
||||
@@ -65,13 +67,15 @@ void main() {
|
||||
|
||||
test("should not have a 0 sized colored Box", () {
|
||||
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(
|
||||
decoration: new BoxDecoration(),
|
||||
child: paddingBox
|
||||
child: paddingBox,
|
||||
);
|
||||
layout(root);
|
||||
expect(coloredBox.size.width, equals(780.0));
|
||||
@@ -80,22 +84,23 @@ void main() {
|
||||
|
||||
test("reparenting should clear position", () {
|
||||
RenderDecoratedBox coloredBox = new RenderDecoratedBox(
|
||||
decoration: new BoxDecoration());
|
||||
decoration: new BoxDecoration(),
|
||||
);
|
||||
|
||||
RenderPadding paddedBox = new RenderPadding(
|
||||
child: coloredBox, padding: const EdgeInsets.all(10.0));
|
||||
|
||||
child: coloredBox,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
);
|
||||
layout(paddedBox);
|
||||
|
||||
BoxParentData parentData = coloredBox.parentData;
|
||||
expect(parentData.offset.dx, isNot(equals(0.0)));
|
||||
|
||||
paddedBox.child = null;
|
||||
|
||||
RenderConstrainedBox constraintedBox = new RenderConstrainedBox(
|
||||
child: coloredBox, additionalConstraints: const BoxConstraints());
|
||||
|
||||
child: coloredBox,
|
||||
additionalConstraints: const BoxConstraints(),
|
||||
);
|
||||
layout(constraintedBox);
|
||||
|
||||
parentData = coloredBox.parentData;
|
||||
expect(parentData.offset.dx, equals(0.0));
|
||||
expect(coloredBox.parentData?.runtimeType, ParentData);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ void layout(RenderBox box, {
|
||||
|
||||
void pumpFrame({ EnginePhase phase: EnginePhase.layout }) {
|
||||
assert(renderer != null);
|
||||
assert(renderer.renderView != null);
|
||||
assert(renderer.renderView.child != null); // call layout() first!
|
||||
renderer.phase = phase;
|
||||
renderer.beginFrame();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user