Unverified Commit cc239580 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Make Hover Listener respect transforms (#32025)

parent ea03ac2b
...@@ -2566,6 +2566,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior { ...@@ -2566,6 +2566,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
void _updateAnnotations() { void _updateAnnotations() {
bool changed = false; bool changed = false;
final bool hadHoverAnnotation = _hoverAnnotation != null;
if (_hoverAnnotation != null && attached) { if (_hoverAnnotation != null && attached) {
RendererBinding.instance.mouseTracker.detachAnnotation(_hoverAnnotation); RendererBinding.instance.mouseTracker.detachAnnotation(_hoverAnnotation);
changed = true; changed = true;
...@@ -2587,6 +2588,10 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior { ...@@ -2587,6 +2588,10 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
if (changed) { if (changed) {
markNeedsPaint(); markNeedsPaint();
} }
final bool hasHoverAnnotation = _hoverAnnotation != null;
if (hadHoverAnnotation != hasHoverAnnotation) {
markNeedsCompositingBitsUpdate();
}
} }
@override @override
...@@ -2608,6 +2613,9 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior { ...@@ -2608,6 +2613,9 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
super.detach(); super.detach();
} }
@override
bool get needsCompositing => _hoverAnnotation != null;
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (_hoverAnnotation != null) { if (_hoverAnnotation != null) {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -232,5 +233,120 @@ void main() { ...@@ -232,5 +233,120 @@ void main() {
expect(tester.binding.mouseTracker.isAnnotationAttached(renderListener1.hoverAnnotation), isFalse); expect(tester.binding.mouseTracker.isAnnotationAttached(renderListener1.hoverAnnotation), isFalse);
expect(tester.binding.mouseTracker.isAnnotationAttached(renderListener2.hoverAnnotation), isFalse); expect(tester.binding.mouseTracker.isAnnotationAttached(renderListener2.hoverAnnotation), isFalse);
}); });
testWidgets('works with transform', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/31986.
final Key key = UniqueKey();
const double scaleFactor = 2.0;
const double localWidth = 150.0;
const double localHeight = 100.0;
final List<PointerEvent> events = <PointerEvent>[];
await tester.pumpWidget(
MaterialApp(
home: Center(
child: Transform.scale(
scale: scaleFactor,
child: Listener(
onPointerEnter: (PointerEnterEvent event) {
events.add(event);
},
onPointerHover: (PointerHoverEvent event) {
events.add(event);
},
onPointerExit: (PointerExitEvent event) {
events.add(event);
},
child: Container(
key: key,
color: Colors.blue,
height: localHeight,
width: localWidth,
child: const Text('Hi'),
),
),
),
),
),
);
final Offset topLeft = tester.getTopLeft(find.byKey(key));
final Offset topRight = tester.getTopRight(find.byKey(key));
final Offset bottomLeft = tester.getBottomLeft(find.byKey(key));
expect(topRight.dx - topLeft.dx, scaleFactor * localWidth);
expect(bottomLeft.dy - topLeft.dy, scaleFactor * localHeight);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.moveTo(topLeft - const Offset(1, 1));
await tester.pump();
expect(events, isEmpty);
await gesture.moveTo(topLeft + const Offset(1, 1));
await tester.pump();
expect(events, hasLength(2));
expect(events.first, isA<PointerEnterEvent>());
expect(events.last, isA<PointerHoverEvent>());
events.clear();
await gesture.moveTo(bottomLeft + const Offset(1, -1));
await tester.pump();
expect(events.single, isA<PointerHoverEvent>());
expect(events.single.delta, const Offset(0.0, scaleFactor * localHeight - 2));
events.clear();
await gesture.moveTo(bottomLeft + const Offset(1, 1));
await tester.pump();
expect(events.single, isA<PointerExitEvent>());
events.clear();
});
testWidgets('needsCompositing updates correctly and is respected', (WidgetTester tester) async {
// Pretend that we have a mouse connected.
final TestGesture gesture = await tester.startGesture(Offset.zero, kind: PointerDeviceKind.mouse);
await gesture.up();
await tester.pumpWidget(
Transform.scale(
scale: 2.0,
child: Listener(
onPointerDown: (PointerDownEvent _) { },
),
),
);
final RenderPointerListener listener = tester.renderObject(find.byType(Listener));
expect(listener.needsCompositing, isFalse);
// No TransformLayer for `Transform.scale` is added because composting is
// not required and therefore the transform is executed on the canvas
// directly. (One TransformLayer is always present for the root
// transform.)
expect(tester.layers.whereType<TransformLayer>(), hasLength(1));
await tester.pumpWidget(
Transform.scale(
scale: 2.0,
child: Listener(
onPointerDown: (PointerDownEvent _) { },
onPointerHover: (PointerHoverEvent _) { },
),
),
);
expect(listener.needsCompositing, isTrue);
// Composting is required, therefore a dedicated TransformLayer for
// `Transform.scale` is added.
expect(tester.layers.whereType<TransformLayer>(), hasLength(2));
await tester.pumpWidget(
Transform.scale(
scale: 2.0,
child: Listener(
onPointerDown: (PointerDownEvent _) { },
),
),
);
expect(listener.needsCompositing, isFalse);
// TransformLayer for `Transform.scale` is removed again as transform is
// executed directly on the canvas.
expect(tester.layers.whereType<TransformLayer>(), hasLength(1));
});
}); });
} }
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