Commit 4c5084cb authored by Adam Barth's avatar Adam Barth Committed by GitHub

Update clip geometry during hitTest if needed (#6091)

The code previously assumed that paint would be called before hitTest, but that
assumption isn't valid because RenderOpacity hit tests its children without
painting them.

Fixes #6089
parent 7238d93e
...@@ -1269,6 +1269,13 @@ abstract class RenderBox extends RenderObject { ...@@ -1269,6 +1269,13 @@ abstract class RenderBox extends RenderObject {
/// The caller is responsible for transforming [position] into the local /// 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. /// whether the given position is within its bounds.
///
/// Hit testing requires layout to be up-to-date but does not require painting
/// to be up-to-date. That means a render object can rely upon [performLayout]
/// having been called in [hitTest] but cannot rely upon [paint] having been
/// called. For example, a render object might be a child of a [RenderOpacity]
/// object, which calls [hitTest] on its children when its opacity is zero
/// even through it does not [paint] its children.
bool hitTest(HitTestResult result, { @required Point position }) { bool hitTest(HitTestResult result, { @required Point position }) {
assert(() { assert(() {
if (needsLayout) { if (needsLayout) {
......
...@@ -941,6 +941,7 @@ class RenderClipRect extends _RenderCustomClip<Rect> { ...@@ -941,6 +941,7 @@ class RenderClipRect extends _RenderCustomClip<Rect> {
@override @override
bool hitTest(HitTestResult result, { Point position }) { bool hitTest(HitTestResult result, { Point position }) {
if (_clipper != null) { if (_clipper != null) {
_updateClip();
assert(_clip != null); assert(_clip != null);
if (!_clip.contains(position)) if (!_clip.contains(position))
return false; return false;
...@@ -1033,6 +1034,7 @@ class RenderClipOval extends _RenderCustomClip<Rect> { ...@@ -1033,6 +1034,7 @@ class RenderClipOval extends _RenderCustomClip<Rect> {
@override @override
bool hitTest(HitTestResult result, { Point position }) { bool hitTest(HitTestResult result, { Point position }) {
_updateClip();
assert(_clip != null); assert(_clip != null);
Point center = _clip.center; Point center = _clip.center;
// convert the position to an offset from the center of the unit circle // convert the position to an offset from the center of the unit circle
...@@ -1082,6 +1084,7 @@ class RenderClipPath extends _RenderCustomClip<Path> { ...@@ -1082,6 +1084,7 @@ class RenderClipPath extends _RenderCustomClip<Path> {
@override @override
bool hitTest(HitTestResult result, { Point position }) { bool hitTest(HitTestResult result, { Point position }) {
if (_clipper != null) { if (_clipper != null) {
_updateClip();
assert(_clip != null); assert(_clip != null);
if (!_clip.contains(position)) if (!_clip.contains(position))
return false; return false;
......
...@@ -78,6 +78,29 @@ void main() { ...@@ -78,6 +78,29 @@ void main() {
log.clear(); log.clear();
}); });
testWidgets('Transparent ClipOval hit test', (WidgetTester tester) async {
await tester.pumpWidget(
new Opacity(
opacity: 0.0,
child: new ClipOval(
child: new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () { log.add('tap'); },
)
)
)
);
expect(log, equals(<String>[]));
await tester.tapAt(new Point(10.0, 10.0));
expect(log, equals(<String>[]));
log.clear();
await tester.tapAt(new Point(400.0, 300.0));
expect(log, equals(<String>['tap']));
log.clear();
});
testWidgets('ClipRect', (WidgetTester tester) async { testWidgets('ClipRect', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new Align( new Align(
......
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