Allow RenderObject.getTransformTo
to take an arbitrary RenderObject in the same tree (#148897)
This is https://github.com/flutter/flutter/pull/130192 but without the additional parameter. Fixes https://github.com/flutter/flutter/issues/146764, https://github.com/flutter/flutter/issues/148410
This commit is contained in:
parent
bb5b7d2608
commit
a766945bdd
@ -3317,12 +3317,21 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
|
||||
}
|
||||
|
||||
/// {@template flutter.rendering.RenderObject.getTransformTo}
|
||||
/// Applies the paint transform up the tree to `ancestor`.
|
||||
/// Applies the paint transform from this [RenderObject] to the `target`
|
||||
/// [RenderObject].
|
||||
///
|
||||
/// Returns a matrix that maps the local paint coordinate system to the
|
||||
/// coordinate system of `ancestor`.
|
||||
/// coordinate system of `target`, or a [Matrix4.zero] if the paint transform
|
||||
/// can not be computed.
|
||||
///
|
||||
/// If `ancestor` is null, this method returns a matrix that maps from the
|
||||
/// This method throws an exception when the `target` is not in the same render
|
||||
/// tree as this [RenderObject], as the behavior is undefined.
|
||||
///
|
||||
/// This method ignores [RenderObject.paintsChild]. This means it will still
|
||||
/// try to compute the paint transform even if [this] or `target` is currently
|
||||
/// not visible.
|
||||
///
|
||||
/// If `target` is null, this method returns a matrix that maps from the
|
||||
/// local paint coordinate system to the coordinate system of the
|
||||
/// [PipelineOwner.rootNode].
|
||||
/// {@endtemplate}
|
||||
@ -3332,30 +3341,61 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
|
||||
/// the global coordinate system in logical pixels. To get physical pixels,
|
||||
/// use [applyPaintTransform] from the [RenderView] to further transform the
|
||||
/// coordinate.
|
||||
Matrix4 getTransformTo(RenderObject? ancestor) {
|
||||
final bool ancestorSpecified = ancestor != null;
|
||||
Matrix4 getTransformTo(RenderObject? target) {
|
||||
assert(attached);
|
||||
if (ancestor == null) {
|
||||
final RenderObject? rootNode = owner!.rootNode;
|
||||
if (rootNode is RenderObject) {
|
||||
ancestor = rootNode;
|
||||
// The paths from to fromRenderObject and toRenderObject's common ancestor.
|
||||
// Each list's length is greater than 1 if not null.
|
||||
//
|
||||
// [this, ...., commonAncestorRenderObject], or null if `this` is the common
|
||||
// ancestor.
|
||||
List<RenderObject>? fromPath;
|
||||
// [target, ...., commonAncestorRenderObject], or null if `target` is the
|
||||
// common ancestor.
|
||||
List<RenderObject>? toPath;
|
||||
|
||||
RenderObject from = this;
|
||||
RenderObject to = target ?? owner!.rootNode!;
|
||||
|
||||
while (!identical(from, to)) {
|
||||
final int fromDepth = from.depth;
|
||||
final int toDepth = to.depth;
|
||||
|
||||
if (fromDepth >= toDepth) {
|
||||
final RenderObject fromParent = from.parent ?? (throw FlutterError('$target and $this are not in the same render tree.'));
|
||||
(fromPath ??= <RenderObject>[this]).add(fromParent);
|
||||
from = fromParent;
|
||||
}
|
||||
if (fromDepth <= toDepth) {
|
||||
final RenderObject toParent = to.parent ?? (throw FlutterError('$target and $this are not in the same render tree.'));
|
||||
assert(target != null, '$this has a depth that is less than or equal to ${owner?.rootNode}');
|
||||
(toPath ??= <RenderObject>[target!]).add(toParent);
|
||||
to = toParent;
|
||||
}
|
||||
}
|
||||
final List<RenderObject> renderers = <RenderObject>[];
|
||||
for (RenderObject renderer = this; renderer != ancestor; renderer = renderer.parent!) {
|
||||
renderers.add(renderer);
|
||||
assert(renderer.parent != null); // Failed to find ancestor in parent chain.
|
||||
}
|
||||
if (ancestorSpecified) {
|
||||
renderers.add(ancestor!);
|
||||
}
|
||||
final Matrix4 transform = Matrix4.identity();
|
||||
for (int index = renderers.length - 1; index > 0; index -= 1) {
|
||||
renderers[index].applyPaintTransform(renderers[index - 1], transform);
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
Matrix4? fromTransform;
|
||||
if (fromPath != null) {
|
||||
assert(fromPath.length > 1);
|
||||
fromTransform = Matrix4.identity();
|
||||
final int lastIndex = target == null ? fromPath.length - 2 : fromPath.length - 1;
|
||||
for (int index = lastIndex; index > 0; index -= 1) {
|
||||
fromPath[index].applyPaintTransform(fromPath[index - 1], fromTransform);
|
||||
}
|
||||
}
|
||||
if (toPath == null) {
|
||||
return fromTransform ?? Matrix4.identity();
|
||||
}
|
||||
|
||||
assert(toPath.length > 1);
|
||||
final Matrix4 toTransform = Matrix4.identity();
|
||||
for (int index = toPath.length - 1; index > 0; index -= 1) {
|
||||
toPath[index].applyPaintTransform(toPath[index - 1], toTransform);
|
||||
}
|
||||
if (toTransform.invert() == 0) { // If the matrix is singular then `invert()` doesn't do anything.
|
||||
return Matrix4.zero();
|
||||
}
|
||||
return (fromTransform?..multiply(toTransform)) ?? toTransform;
|
||||
}
|
||||
|
||||
/// Returns a rect in this object's coordinate system that describes
|
||||
/// the approximate bounding box of the clip rect that would be
|
||||
|
@ -156,7 +156,7 @@ void main() {
|
||||
expect(() => data3.detach(), throwsAssertionError);
|
||||
});
|
||||
|
||||
test('RenderObject.getTransformTo asserts is argument is not descendant', () {
|
||||
test('RenderObject.getTransformTo asserts if target not in the same render tree', () {
|
||||
final PipelineOwner owner = PipelineOwner();
|
||||
final TestRenderObject renderObject1 = TestRenderObject();
|
||||
renderObject1.attach(owner);
|
||||
@ -165,6 +165,64 @@ void main() {
|
||||
expect(() => renderObject1.getTransformTo(renderObject2), throwsAssertionError);
|
||||
});
|
||||
|
||||
test('RenderObject.getTransformTo works for siblings and descendants', () {
|
||||
final PipelineOwner owner = PipelineOwner();
|
||||
final TestRenderObject renderObject1 = TestRenderObject()..attach(owner);
|
||||
final TestRenderObject renderObject11 = TestRenderObject();
|
||||
final TestRenderObject renderObject12 = TestRenderObject();
|
||||
|
||||
renderObject1
|
||||
..add(renderObject11)
|
||||
..add(renderObject12);
|
||||
expect(renderObject11.getTransformTo(renderObject12), equals(Matrix4.identity()));
|
||||
expect(renderObject1.getTransformTo(renderObject11), equals(Matrix4.identity()));
|
||||
expect(renderObject1.getTransformTo(renderObject12), equals(Matrix4.identity()));
|
||||
expect(renderObject11.getTransformTo(renderObject1), equals(Matrix4.identity()));
|
||||
expect(renderObject12.getTransformTo(renderObject1), equals(Matrix4.identity()));
|
||||
|
||||
expect(renderObject1.getTransformTo(renderObject1), equals(Matrix4.identity()));
|
||||
expect(renderObject11.getTransformTo(renderObject11), equals(Matrix4.identity()));
|
||||
expect(renderObject12.getTransformTo(renderObject12), equals(Matrix4.identity()));
|
||||
});
|
||||
|
||||
test('RenderObject.getTransformTo gets the correct paint transform', () {
|
||||
final PipelineOwner owner = PipelineOwner();
|
||||
final TestRenderObject renderObject0 = TestRenderObject()
|
||||
..attach(owner);
|
||||
final TestRenderObject renderObject1 = TestRenderObject();
|
||||
final TestRenderObject renderObject2 = TestRenderObject();
|
||||
renderObject0
|
||||
..add(renderObject1)
|
||||
..add(renderObject2)
|
||||
..paintTransform = Matrix4.diagonal3Values(9, 4, 1);
|
||||
|
||||
final TestRenderObject renderObject11 = TestRenderObject();
|
||||
final TestRenderObject renderObject21 = TestRenderObject();
|
||||
renderObject1
|
||||
..add(renderObject11)
|
||||
..paintTransform = Matrix4.translationValues(8, 16, 32);
|
||||
renderObject2
|
||||
..add(renderObject21)
|
||||
..paintTransform = Matrix4.translationValues(32, 64, 128);
|
||||
|
||||
expect(
|
||||
renderObject11.getTransformTo(renderObject21),
|
||||
equals(Matrix4.translationValues((8 - 32) * 9, (16 - 64) * 4, 32 - 128)),
|
||||
);
|
||||
// Turn one of the paint transforms into a singular matrix and getTransformTo
|
||||
// should return Matrix4.zero().
|
||||
renderObject0.paintTransform = Matrix4(
|
||||
1, 1, 1 ,1,
|
||||
2, 2, 2, 2,
|
||||
3, 3, 3, 3,
|
||||
4, 4, 4, 4,
|
||||
); // Not a full rank matrix, so it has to be singular.
|
||||
expect(
|
||||
renderObject11.getTransformTo(renderObject21),
|
||||
equals(Matrix4.zero()),
|
||||
);
|
||||
});
|
||||
|
||||
test('PaintingContext.pushClipRect reuses the layer', () {
|
||||
_testPaintingContextLayerReuse<ClipRectLayer>((PaintingContextCallback painter, PaintingContext context, Offset offset, Layer? oldLayer) {
|
||||
return context.pushClipRect(true, offset, Rect.zero, painter, oldLayer: oldLayer as ClipRectLayer?);
|
||||
@ -376,7 +434,8 @@ class _TestCustomLayerBox extends RenderBox {
|
||||
|
||||
class TestParentData extends ParentData with ContainerParentDataMixin<RenderBox> { }
|
||||
|
||||
class TestRenderObject extends RenderObject {
|
||||
class TestRenderObjectParentData extends ParentData with ContainerParentDataMixin<TestRenderObject> { }
|
||||
class TestRenderObject extends RenderObject with ContainerRenderObjectMixin<TestRenderObject, TestRenderObjectParentData> {
|
||||
TestRenderObject({this.allowPaintBounds = false});
|
||||
|
||||
final bool allowPaintBounds;
|
||||
@ -393,6 +452,20 @@ class TestRenderObject extends RenderObject {
|
||||
return Rect.zero;
|
||||
}
|
||||
|
||||
Matrix4 paintTransform = Matrix4.identity();
|
||||
@override
|
||||
void applyPaintTransform(covariant RenderObject child, Matrix4 transform) {
|
||||
super.applyPaintTransform(child, transform);
|
||||
transform.multiply(paintTransform);
|
||||
}
|
||||
|
||||
@override
|
||||
void setupParentData(RenderObject child) {
|
||||
if (child.parentData is! TestRenderObjectParentData) {
|
||||
child.parentData = TestRenderObjectParentData();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() { }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user