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
This commit is contained in:
@@ -2074,6 +2074,15 @@ class RenderTransform extends RenderProxyBox {
|
||||
|
||||
@override
|
||||
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) {
|
||||
final Matrix4 inverse = Matrix4.tryInvert(_effectiveTransform);
|
||||
if (inverse == null) {
|
||||
@@ -2083,7 +2092,7 @@ class RenderTransform extends RenderProxyBox {
|
||||
}
|
||||
position = MatrixUtils.transformPoint(inverse, position);
|
||||
}
|
||||
return super.hitTest(result, position: position);
|
||||
return super.hitTestChildren(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -2254,7 +2263,7 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTest(HitTestResult result, { Offset position }) {
|
||||
bool hitTestChildren(HitTestResult result, { Offset position }) {
|
||||
if (size.isEmpty)
|
||||
return false;
|
||||
_updatePaintData();
|
||||
@@ -2265,7 +2274,7 @@ class RenderFittedBox extends RenderProxyBox {
|
||||
return false;
|
||||
}
|
||||
position = MatrixUtils.transformPoint(inverse, position);
|
||||
return super.hitTest(result, position: position);
|
||||
return super.hitTestChildren(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -2322,6 +2331,15 @@ class RenderFractionalTranslation extends RenderProxyBox {
|
||||
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
|
||||
/// child as it is painted. When set to false, hit tests are performed
|
||||
/// ignoring the transformation.
|
||||
@@ -2331,7 +2349,7 @@ class RenderFractionalTranslation extends RenderProxyBox {
|
||||
bool transformHitTests;
|
||||
|
||||
@override
|
||||
bool hitTest(HitTestResult result, { Offset position }) {
|
||||
bool hitTestChildren(HitTestResult result, { Offset position }) {
|
||||
assert(!debugNeedsLayout);
|
||||
if (transformHitTests) {
|
||||
position = new Offset(
|
||||
@@ -2339,7 +2357,7 @@ class RenderFractionalTranslation extends RenderProxyBox {
|
||||
position.dy - translation.dy * size.height,
|
||||
);
|
||||
}
|
||||
return super.hitTest(result, position: position);
|
||||
return super.hitTestChildren(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -4174,6 +4192,15 @@ class RenderFollowerLayer extends RenderProxyBox {
|
||||
|
||||
@override
|
||||
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());
|
||||
if (inverse == null) {
|
||||
// We cannot invert the effective transform. That means the child
|
||||
@@ -4181,7 +4208,7 @@ class RenderFollowerLayer extends RenderProxyBox {
|
||||
return false;
|
||||
}
|
||||
position = MatrixUtils.transformPoint(inverse, position);
|
||||
return super.hitTest(result, position: position);
|
||||
return super.hitTestChildren(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -333,4 +333,29 @@ void main() {
|
||||
-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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user