Unverified Commit c4d6311a authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Fix #16464

* Fix #16464: Pass hit test when a big child is inside RenderFittedBox, RenderTransform, RenderFractionalTranslation, or RenderFollowerLayer

* Override `hitTestChildren`

* RenderTransform and RenderFollowerLayer shouldn't check if they are hit
themselves

* Test the hit test for translated child into translated box

* Add hit test for `FractionalTranslation`

* Don't check if RenderFractionalTranslation is hit themself

* Add hit test for FractionalTranslation

* Add test for FractionalTranslation
parent 677df7c3
...@@ -2074,6 +2074,15 @@ class RenderTransform extends RenderProxyBox { ...@@ -2074,6 +2074,15 @@ class RenderTransform extends RenderProxyBox {
@override @override
bool hitTest(HitTestResult result, { Offset position }) { bool hitTest(HitTestResult result, { Offset position }) {
// RenderTransform objects don't check if they are
// themselves hit, because it's confusing to think about
// how the untransformed size and the child's transformed
// position interact.
return hitTestChildren(result, position: position);
}
@override
bool hitTestChildren(HitTestResult result, { Offset position }) {
if (transformHitTests) { if (transformHitTests) {
final Matrix4 inverse = Matrix4.tryInvert(_effectiveTransform); final Matrix4 inverse = Matrix4.tryInvert(_effectiveTransform);
if (inverse == null) { if (inverse == null) {
...@@ -2083,7 +2092,7 @@ class RenderTransform extends RenderProxyBox { ...@@ -2083,7 +2092,7 @@ class RenderTransform extends RenderProxyBox {
} }
position = MatrixUtils.transformPoint(inverse, position); position = MatrixUtils.transformPoint(inverse, position);
} }
return super.hitTest(result, position: position); return super.hitTestChildren(result, position: position);
} }
@override @override
...@@ -2254,7 +2263,7 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -2254,7 +2263,7 @@ class RenderFittedBox extends RenderProxyBox {
} }
@override @override
bool hitTest(HitTestResult result, { Offset position }) { bool hitTestChildren(HitTestResult result, { Offset position }) {
if (size.isEmpty) if (size.isEmpty)
return false; return false;
_updatePaintData(); _updatePaintData();
...@@ -2265,7 +2274,7 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -2265,7 +2274,7 @@ class RenderFittedBox extends RenderProxyBox {
return false; return false;
} }
position = MatrixUtils.transformPoint(inverse, position); position = MatrixUtils.transformPoint(inverse, position);
return super.hitTest(result, position: position); return super.hitTestChildren(result, position: position);
} }
@override @override
...@@ -2322,6 +2331,15 @@ class RenderFractionalTranslation extends RenderProxyBox { ...@@ -2322,6 +2331,15 @@ class RenderFractionalTranslation extends RenderProxyBox {
markNeedsPaint(); markNeedsPaint();
} }
@override
bool hitTest(HitTestResult result, { Offset position }) {
// RenderFractionalTranslation objects don't check if they are
// themselves hit, because it's confusing to think about
// how the untransformed size and the child's transformed
// position interact.
return hitTestChildren(result, position: position);
}
/// When set to true, hit tests are performed based on the position of the /// When set to true, hit tests are performed based on the position of the
/// child as it is painted. When set to false, hit tests are performed /// child as it is painted. When set to false, hit tests are performed
/// ignoring the transformation. /// ignoring the transformation.
...@@ -2331,7 +2349,7 @@ class RenderFractionalTranslation extends RenderProxyBox { ...@@ -2331,7 +2349,7 @@ class RenderFractionalTranslation extends RenderProxyBox {
bool transformHitTests; bool transformHitTests;
@override @override
bool hitTest(HitTestResult result, { Offset position }) { bool hitTestChildren(HitTestResult result, { Offset position }) {
assert(!debugNeedsLayout); assert(!debugNeedsLayout);
if (transformHitTests) { if (transformHitTests) {
position = new Offset( position = new Offset(
...@@ -2339,7 +2357,7 @@ class RenderFractionalTranslation extends RenderProxyBox { ...@@ -2339,7 +2357,7 @@ class RenderFractionalTranslation extends RenderProxyBox {
position.dy - translation.dy * size.height, position.dy - translation.dy * size.height,
); );
} }
return super.hitTest(result, position: position); return super.hitTestChildren(result, position: position);
} }
@override @override
...@@ -4174,6 +4192,15 @@ class RenderFollowerLayer extends RenderProxyBox { ...@@ -4174,6 +4192,15 @@ class RenderFollowerLayer extends RenderProxyBox {
@override @override
bool hitTest(HitTestResult result, { Offset position }) { bool hitTest(HitTestResult result, { Offset position }) {
// RenderFollowerLayer objects don't check if they are
// themselves hit, because it's confusing to think about
// how the untransformed size and the child's transformed
// position interact.
return hitTestChildren(result, position: position);
}
@override
bool hitTestChildren(HitTestResult result, { Offset position }) {
final Matrix4 inverse = Matrix4.tryInvert(getCurrentTransform()); final Matrix4 inverse = Matrix4.tryInvert(getCurrentTransform());
if (inverse == null) { if (inverse == null) {
// We cannot invert the effective transform. That means the child // We cannot invert the effective transform. That means the child
...@@ -4181,7 +4208,7 @@ class RenderFollowerLayer extends RenderProxyBox { ...@@ -4181,7 +4208,7 @@ class RenderFollowerLayer extends RenderProxyBox {
return false; return false;
} }
position = MatrixUtils.transformPoint(inverse, position); position = MatrixUtils.transformPoint(inverse, position);
return super.hitTest(result, position: position); return super.hitTestChildren(result, position: position);
} }
@override @override
......
...@@ -54,6 +54,97 @@ void main() { ...@@ -54,6 +54,97 @@ void main() {
}); });
group('FractionalTranslation', () {
testWidgets('hit test - entirely inside the bounding box', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
new Center(
child: new FractionalTranslation(
translation: Offset.zero,
transformHitTests: true,
child: new Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: new SizedBox(
key: key1,
width: 100.0,
height: 100.0,
child: new Container(
color: const Color(0xFF0000FF)
),
),
)
)
)
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
testWidgets('hit test - partially inside the bounding box', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
new Center(
child: new FractionalTranslation(
translation: const Offset(0.5, 0.5),
transformHitTests: true,
child: new Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: new SizedBox(
key: key1,
width: 100.0,
height: 100.0,
child: new Container(
color: const Color(0xFF0000FF)
),
),
)
)
)
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
testWidgets('hit test - completely outside the bounding box', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
new Center(
child: new FractionalTranslation(
translation: const Offset(1.0, 1.0),
transformHitTests: true,
child: new Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: new SizedBox(
key: key1,
width: 100.0,
height: 100.0,
child: new Container(
color: const Color(0xFF0000FF)
),
),
)
)
)
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
});
} }
HitsRenderBox hits(RenderBox renderBox) => new HitsRenderBox(renderBox); HitsRenderBox hits(RenderBox renderBox) => new HitsRenderBox(renderBox);
......
...@@ -439,6 +439,39 @@ void main() { ...@@ -439,6 +439,39 @@ void main() {
} }
} }
}); });
testWidgets('Big child into small fitted box - hit testing', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
new Center(
child: new SizedBox(
width: 100.0,
height: 100.0,
child: new FittedBox(
fit: BoxFit.contain,
alignment: FractionalOffset.center,
child: new SizedBox(
width: 1000.0,
height: 1000.0,
child: new Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: new Container(
key: key1,
color: const Color(0xFF000000),
)
)
)
)
)
)
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
} }
List<Type> getLayers() { List<Type> getLayers() {
......
...@@ -333,4 +333,29 @@ void main() { ...@@ -333,4 +333,29 @@ void main() {
-400.0, -300.0, 0.0, 1.0, // it's 1600x1200, centered in an 800x600 square -400.0, -300.0, 0.0, 1.0, // it's 1600x1200, centered in an 800x600 square
]); ]);
}); });
testWidgets('Translated child into translated box - hit test', (WidgetTester tester) async {
final GlobalKey key1 = new GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
new Transform.translate(
offset: const Offset(100.0, 50.0),
child: new Transform.translate(
offset: const Offset(1000.0, 1000.0),
child: new Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: new Container(
key: key1,
color: const Color(0xFF000000),
)
)
)
),
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
} }
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