From c4d6311a29dc71be073f305f34a6ee2198ccd702 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Thu, 24 May 2018 23:16:16 -0700 Subject: [PATCH] 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 --- .../flutter/lib/src/rendering/proxy_box.dart | 39 ++++++-- packages/flutter/test/widgets/basic_test.dart | 91 +++++++++++++++++++ .../flutter/test/widgets/fitted_box_test.dart | 33 +++++++ .../flutter/test/widgets/transform_test.dart | 25 +++++ 4 files changed, 182 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index 6060a0ccaf..9637146901 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -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 diff --git a/packages/flutter/test/widgets/basic_test.dart b/packages/flutter/test/widgets/basic_test.dart index 02663d2847..7be6fe3a73 100644 --- a/packages/flutter/test/widgets/basic_test.dart +++ b/packages/flutter/test/widgets/basic_test.dart @@ -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); diff --git a/packages/flutter/test/widgets/fitted_box_test.dart b/packages/flutter/test/widgets/fitted_box_test.dart index f53d383f6d..3217add1ae 100644 --- a/packages/flutter/test/widgets/fitted_box_test.dart +++ b/packages/flutter/test/widgets/fitted_box_test.dart @@ -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 getLayers() { diff --git a/packages/flutter/test/widgets/transform_test.dart b/packages/flutter/test/widgets/transform_test.dart index 2c890d29f8..c009edd481 100644 --- a/packages/flutter/test/widgets/transform_test.dart +++ b/packages/flutter/test/widgets/transform_test.dart @@ -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); + }); }