Unverified Commit f1d8e30c authored by xubaolin's avatar xubaolin Committed by GitHub

Add `HitTestBehavior` property to `MouseRegion` (#100405)

parent 654ccf35
...@@ -2958,7 +2958,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior { ...@@ -2958,7 +2958,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
/// ///
/// * [MouseRegion], a widget that listens to hover events using /// * [MouseRegion], a widget that listens to hover events using
/// [RenderMouseRegion]. /// [RenderMouseRegion].
class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation { class RenderMouseRegion extends RenderProxyBoxWithHitTestBehavior implements MouseTrackerAnnotation {
/// Creates a render object that forwards pointer events to callbacks. /// Creates a render object that forwards pointer events to callbacks.
/// ///
/// All parameters are optional. By default this method creates an opaque /// All parameters are optional. By default this method creates an opaque
...@@ -2972,16 +2972,13 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation ...@@ -2972,16 +2972,13 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
bool validForMouseTracker = true, bool validForMouseTracker = true,
bool opaque = true, bool opaque = true,
RenderBox? child, RenderBox? child,
HitTestBehavior? hitTestBehavior = HitTestBehavior.opaque,
}) : assert(opaque != null), }) : assert(opaque != null),
assert(cursor != null), assert(cursor != null),
_cursor = cursor, _cursor = cursor,
_validForMouseTracker = validForMouseTracker, _validForMouseTracker = validForMouseTracker,
_opaque = opaque, _opaque = opaque,
super(child); super(behavior: hitTestBehavior ?? HitTestBehavior.opaque, child: child);
@protected
@override
bool hitTestSelf(Offset position) => true;
@override @override
bool hitTest(BoxHitTestResult result, { required Offset position }) { bool hitTest(BoxHitTestResult result, { required Offset position }) {
...@@ -3019,6 +3016,19 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation ...@@ -3019,6 +3016,19 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
} }
} }
/// How to behave during hit testing.
///
/// This defaults to [HitTestBehavior.opaque] if null.
HitTestBehavior? get hitTestBehavior => behavior;
set hitTestBehavior(HitTestBehavior? value) {
final HitTestBehavior newValue = value ?? HitTestBehavior.opaque;
if (behavior != newValue) {
behavior = newValue;
// Trigger [MouseTracker]'s device update to recalculate mouse states.
markNeedsPaint();
}
}
@override @override
PointerEnterEventListener? onEnter; PointerEnterEventListener? onEnter;
......
...@@ -6180,6 +6180,7 @@ class MouseRegion extends SingleChildRenderObjectWidget { ...@@ -6180,6 +6180,7 @@ class MouseRegion extends SingleChildRenderObjectWidget {
this.onHover, this.onHover,
this.cursor = MouseCursor.defer, this.cursor = MouseCursor.defer,
this.opaque = true, this.opaque = true,
this.hitTestBehavior,
Widget? child, Widget? child,
}) : assert(cursor != null), }) : assert(cursor != null),
assert(opaque != null), assert(opaque != null),
...@@ -6330,6 +6331,11 @@ class MouseRegion extends SingleChildRenderObjectWidget { ...@@ -6330,6 +6331,11 @@ class MouseRegion extends SingleChildRenderObjectWidget {
/// This defaults to true. /// This defaults to true.
final bool opaque; final bool opaque;
/// How to behave during hit testing.
///
/// This defaults to [HitTestBehavior.opaque] if null.
final HitTestBehavior? hitTestBehavior;
@override @override
RenderMouseRegion createRenderObject(BuildContext context) { RenderMouseRegion createRenderObject(BuildContext context) {
return RenderMouseRegion( return RenderMouseRegion(
...@@ -6338,6 +6344,7 @@ class MouseRegion extends SingleChildRenderObjectWidget { ...@@ -6338,6 +6344,7 @@ class MouseRegion extends SingleChildRenderObjectWidget {
onExit: onExit, onExit: onExit,
cursor: cursor, cursor: cursor,
opaque: opaque, opaque: opaque,
hitTestBehavior: hitTestBehavior,
); );
} }
...@@ -6348,7 +6355,8 @@ class MouseRegion extends SingleChildRenderObjectWidget { ...@@ -6348,7 +6355,8 @@ class MouseRegion extends SingleChildRenderObjectWidget {
..onHover = onHover ..onHover = onHover
..onExit = onExit ..onExit = onExit
..cursor = cursor ..cursor = cursor
..opaque = opaque; ..opaque = opaque
..hitTestBehavior = hitTestBehavior;
} }
@override @override
......
...@@ -76,6 +76,110 @@ class _HoverFeedbackState extends State<HoverFeedback> { ...@@ -76,6 +76,110 @@ class _HoverFeedbackState extends State<HoverFeedback> {
} }
void main() { void main() {
// Regression test for https://github.com/flutter/flutter/issues/73330
testWidgets('hitTestBehavior test - HitTestBehavior.deferToChild/opaque', (WidgetTester tester) async {
bool onEnter = false;
await tester.pumpWidget(Center(
child: MouseRegion(
hitTestBehavior: HitTestBehavior.deferToChild,
onEnter: (_) => onEnter = true,
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await tester.pump();
// The child is null, so `onEnter` does not trigger.
expect(onEnter, false);
// Update to the default value `HitTestBehavior.opaque`
await tester.pumpWidget(Center(
child: MouseRegion(
onEnter: (_) => onEnter = true,
),
));
expect(onEnter, true);
});
testWidgets('hitTestBehavior test - HitTestBehavior.deferToChild and non-opaque', (WidgetTester tester) async {
bool onEnterRegion1 = false;
bool onEnterRegion2 = false;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
SizedBox(
width: 50.0,
height: 50.0,
child: MouseRegion(
onEnter: (_) => onEnterRegion1 = true,
),
),
SizedBox(
width: 50.0,
height: 50.0,
child: MouseRegion(
opaque: false,
hitTestBehavior: HitTestBehavior.deferToChild,
onEnter: (_) => onEnterRegion2 = true,
child: Container(
color: const Color.fromARGB(0xff, 0xff, 0x10, 0x19),
width: 50.0,
height: 50.0,
),
),
),
],
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await tester.pump();
expect(onEnterRegion2, true);
expect(onEnterRegion1, true);
});
testWidgets('hitTestBehavior test - HitTestBehavior.translucent', (WidgetTester tester) async {
bool onEnterRegion1 = false;
bool onEnterRegion2 = false;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
SizedBox(
width: 50.0,
height: 50.0,
child: MouseRegion(
onEnter: (_) => onEnterRegion1 = true,
),
),
SizedBox(
width: 50.0,
height: 50.0,
child: MouseRegion(
hitTestBehavior: HitTestBehavior.translucent,
onEnter: (_) => onEnterRegion2 = true,
),
),
],
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await tester.pump();
expect(onEnterRegion2, true);
expect(onEnterRegion1, true);
});
testWidgets('onEnter and onExit can be triggered with mouse buttons pressed', (WidgetTester tester) async { testWidgets('onEnter and onExit can be triggered with mouse buttons pressed', (WidgetTester tester) async {
PointerEnterEvent? enter; PointerEnterEvent? enter;
PointerExitEvent? exit; PointerExitEvent? exit;
...@@ -1706,6 +1810,7 @@ void main() { ...@@ -1706,6 +1810,7 @@ void main() {
'parentData: MISSING', 'parentData: MISSING',
'constraints: MISSING', 'constraints: MISSING',
'size: MISSING', 'size: MISSING',
'behavior: opaque',
'listeners: <none>', 'listeners: <none>',
]); ]);
}); });
...@@ -1727,6 +1832,7 @@ void main() { ...@@ -1727,6 +1832,7 @@ void main() {
'parentData: MISSING', 'parentData: MISSING',
'constraints: MISSING', 'constraints: MISSING',
'size: MISSING', 'size: MISSING',
'behavior: opaque',
'listeners: enter, hover, exit', 'listeners: enter, hover, exit',
'cursor: SystemMouseCursor(click)', 'cursor: SystemMouseCursor(click)',
'invalid for MouseTracker', 'invalid for MouseTracker',
......
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