Unverified Commit a71d69ba authored by Tong Mu's avatar Tong Mu Committed by GitHub

Add opacity control to MouseRegion. Add findAnnotations to Layer. (#37896)

* Adds a new parameter bool opaque to MouseRegion
* The same to RenderMouseRegion and AnnotatedRegionLayer
* Add findAnnotations to Layer 
parent 6b4a10ae
......@@ -54,7 +54,7 @@ class HitTestEntry {
final HitTestTarget target;
@override
String toString() => '$target';
String toString() => '${describeIdentity(this)}($target)';
/// Returns a matrix describing how [PointerEvent]s delivered to this
/// [HitTestEntry] should be transformed from the global coordinate space of
......
......@@ -878,9 +878,6 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
}
}
void _handleMouseEnter(PointerEnterEvent event) => _handleHover(true);
void _handleMouseExit(PointerExitEvent event) => _handleHover(false);
void _handleHover(bool hovering) {
if (hovering != _isHovering) {
setState(() {
......@@ -1007,8 +1004,8 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
return IgnorePointer(
ignoring: !_isEnabled,
child: MouseRegion(
onEnter: _handleMouseEnter,
onExit: _handleMouseExit,
onEnter: (PointerEnterEvent event) => _handleHover(true),
onExit: (PointerExitEvent event) => _handleHover(false),
child: AnimatedBuilder(
animation: controller, // changes the _currentLength
builder: (BuildContext context, Widget child) {
......
......@@ -2591,8 +2591,9 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
/// Calls callbacks in response to pointer events that are exclusive to mice.
///
/// Simply put, it responds to events that are related to hovering,
/// i.e. when the mouse enters, exits or hovers a region without pressing.
/// It responds to events that are related to hovering, i.e. when the mouse
/// enters, exits (with or without pressing buttons), or moves over a region
/// without pressing buttons.
///
/// It does not respond to common events that construct gestures, such as when
/// the pointer is pressed, moved, then released or canceled. For these events,
......@@ -2601,14 +2602,21 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
/// If it has a child, it defers to the child for sizing behavior.
///
/// If it does not have a child, it grows to fit the parent-provided constraints.
///
/// See also:
///
/// * [MouseRegion], a widget that listens to hover events using
/// [RenderMouseRegion].
class RenderMouseRegion extends RenderProxyBox {
/// Creates a render object that forwards pointer events to callbacks.
RenderMouseRegion({
PointerEnterEventListener onEnter,
PointerHoverEventListener onHover,
PointerExitEventListener onExit,
this.opaque = true,
RenderBox child,
}) : _onEnter = onEnter,
}) : assert(opaque != null),
_onEnter = onEnter,
_onHover = onHover,
_onExit = onExit,
_annotationIsActive = false,
......@@ -2620,10 +2628,24 @@ class RenderMouseRegion extends RenderProxyBox {
);
}
/// Called when a hovering pointer enters the region for this widget.
/// Whether this object should prevent [RenderMouseRegion]s visually behind it
/// from detecting the pointer, thus affecting how their [onHover], [onEnter],
/// and [onExit] behave.
///
/// If [opaque] is true, this object will absorb the mouse pointer and
/// prevent this object's siblings (or any other objects that are not
/// ancestors or descendants of this object) from detecting the mouse
/// pointer even when the pointer is within their areas.
///
/// If this is a mouse pointer, this will fire when the mouse pointer enters
/// the region defined by this widget.
/// If [opaque] is false, this object will not affect how [RenderMouseRegion]s
/// behind it behave, which will detect the mouse pointer as long as the
/// pointer is within their areas.
///
/// This defaults to true.
bool opaque;
/// Called when a mouse pointer enters the region (with or without buttons
/// pressed).
PointerEnterEventListener get onEnter => _onEnter;
set onEnter(PointerEnterEventListener value) {
if (_onEnter != value) {
......@@ -2637,10 +2659,8 @@ class RenderMouseRegion extends RenderProxyBox {
_onEnter(event);
}
/// Called when a pointer that has not triggered an [onPointerDown] changes
/// position.
///
/// Typically only triggered for mouse pointers.
/// Called when a pointer changes position without buttons pressed and the end
/// position is within the region.
PointerHoverEventListener get onHover => _onHover;
set onHover(PointerHoverEventListener value) {
if (_onHover != value) {
......@@ -2654,10 +2674,7 @@ class RenderMouseRegion extends RenderProxyBox {
_onHover(event);
}
/// Called when a hovering pointer leaves the region for this widget.
///
/// If this is a mouse pointer, this will fire when the mouse pointer leaves
/// the region defined by this widget.
/// Called when a pointer leaves the region (with or without buttons pressed).
PointerExitEventListener get onExit => _onExit;
set onExit(PointerExitEventListener value) {
if (_onExit != value) {
......@@ -2754,6 +2771,7 @@ class RenderMouseRegion extends RenderProxyBox {
_hoverAnnotation,
size: size,
offset: offset,
opaque: opaque,
);
context.pushLayer(layer, super.paint, offset);
} else {
......@@ -2778,6 +2796,7 @@ class RenderMouseRegion extends RenderProxyBox {
},
ifEmpty: '<none>',
));
properties.add(DiagnosticsProperty<bool>('opaque', opaque, defaultValue: true));
}
}
......
......@@ -196,7 +196,9 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
// Layer hit testing is done using device pixels, so we have to convert
// the logical coordinates of the event location back to device pixels
// here.
return layer.findAll<MouseTrackerAnnotation>(position * configuration.devicePixelRatio);
return layer.findAll<MouseTrackerAnnotation>(
position * configuration.devicePixelRatio
).annotations;
}
@override
......@@ -241,12 +243,12 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
final Rect bounds = paintBounds;
final Offset top = Offset(bounds.center.dx, _window.padding.top / _window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - _window.padding.bottom / _window.devicePixelRatio);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top)?.annotation;
// Only android has a customizable system navigation bar.
SystemUiOverlayStyle lowerOverlayStyle;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
lowerOverlayStyle = layer.find<SystemUiOverlayStyle>(bottom);
lowerOverlayStyle = layer.find<SystemUiOverlayStyle>(bottom)?.annotation;
break;
case TargetPlatform.iOS:
case TargetPlatform.fuchsia:
......
......@@ -5575,11 +5575,11 @@ class Listener extends StatelessWidget {
// TODO(tongmu): After it goes stable, remove these 3 parameters from Listener
// and Listener should no longer need an intermediate class _PointerListener.
// https://github.com/flutter/flutter/issues/36085
@Deprecated('Use MouseRegion.onEnter instead')
@Deprecated('Use MouseRegion.onEnter instead. See MouseRegion.opaque for behavioral difference.')
this.onPointerEnter, // ignore: deprecated_member_use_from_same_package
@Deprecated('Use MouseRegion.onExit instead')
@Deprecated('Use MouseRegion.onExit instead. See MouseRegion.opaque for behavioral difference.')
this.onPointerExit, // ignore: deprecated_member_use_from_same_package
@Deprecated('Use MouseRegion.onHover instead')
@Deprecated('Use MouseRegion.onHover instead. See MouseRegion.opaque for behavioral difference.')
this.onPointerHover, // ignore: deprecated_member_use_from_same_package
this.onPointerUp,
this.onPointerCancel,
......@@ -5656,6 +5656,7 @@ class Listener extends StatelessWidget {
onEnter: onPointerEnter,
onExit: onPointerExit,
onHover: onPointerHover,
opaque: false,
child: result,
);
}
......@@ -5815,8 +5816,10 @@ class MouseRegion extends SingleChildRenderObjectWidget {
this.onEnter,
this.onExit,
this.onHover,
this.opaque = true,
Widget child,
}) : super(key: key, child: child);
}) : assert(opaque != null),
super(key: key, child: child);
/// Called when a mouse pointer (with or without buttons pressed) enters the
/// region defined by this widget, or when the widget appears under the
......@@ -5832,6 +5835,22 @@ class MouseRegion extends SingleChildRenderObjectWidget {
/// the pointer.
final PointerExitEventListener onExit;
/// Whether this widget should prevent other [MouseRegion]s visually behind it
/// from detecting the pointer, thus affecting how their [onHover], [onEnter],
/// and [onExit] behave.
///
/// If [opaque] is true, this widget will absorb the mouse pointer and
/// prevent this widget's siblings (or any other widgets that are not
/// ancestors or descendants of this widget) from detecting the mouse
/// pointer even when the pointer is within their areas.
///
/// If [opaque] is false, this object will not affect how [MouseRegion]s
/// behind it behave, which will detect the mouse pointer as long as the
/// pointer is within their areas.
///
/// This defaults to true.
final bool opaque;
@override
_MouseRegionElement createElement() => _MouseRegionElement(this);
......@@ -5841,6 +5860,7 @@ class MouseRegion extends SingleChildRenderObjectWidget {
onEnter: onEnter,
onHover: onHover,
onExit: onExit,
opaque: opaque,
);
}
......@@ -5849,7 +5869,8 @@ class MouseRegion extends SingleChildRenderObjectWidget {
renderObject
..onEnter = onEnter
..onHover = onHover
..onExit = onExit;
..onExit = onExit
..opaque = opaque;
}
@override
......@@ -5863,6 +5884,7 @@ class MouseRegion extends SingleChildRenderObjectWidget {
if (onHover != null)
listeners.add('hover');
properties.add(IterableProperty<String>('listeners', listeners, ifEmpty: '<none>'));
properties.add(DiagnosticsProperty<bool>('opaque', opaque, defaultValue: true));
}
}
......
......@@ -57,10 +57,14 @@ class _ProxyLayer extends Layer {
}
@override
S find<S>(Offset regionOffset) => _layer.find(regionOffset);
@override
Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
@protected
bool findAnnotations<S>(
AnnotationResult<S> result,
Offset localPosition, {
@required bool onlyFirst,
}) {
return _layer.findAnnotations(result, localPosition, onlyFirst: onlyFirst);
}
}
/// A [Canvas] that multicasts all method calls to a main canvas and a
......@@ -2662,10 +2666,14 @@ class _InspectorOverlayLayer extends Layer {
}
@override
S find<S>(Offset regionOffset) => null;
@override
Iterable<S> findAll<S>(Offset regionOffset) => <S>[];
@protected
bool findAnnotations<S>(
AnnotationResult<S> result,
Offset localPosition, {
bool onlyFirst,
}) {
return false;
}
}
const double _kScreenEdgeMargin = 10.0;
......
......@@ -22,9 +22,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(0.0, 1.0)), 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)), 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)), 2);
expect(containerLayer.find<int>(const Offset(0.0, 1.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)).annotation, 2);
});
test('finds a value within the clip in a ClipRectLayer', () {
......@@ -41,9 +41,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(0.0, 1.0)), 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)), 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)), 2);
expect(containerLayer.find<int>(const Offset(0.0, 1.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)).annotation, 2);
});
......@@ -61,9 +61,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(5.0, 5.0)), 0);
expect(containerLayer.find<int>(const Offset(5.0, 105.0)), 1);
expect(containerLayer.find<int>(const Offset(5.0, 205.0)), 2);
expect(containerLayer.find<int>(const Offset(5.0, 5.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(5.0, 105.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(5.0, 205.0)).annotation, 2);
});
test('finds a value under a TransformLayer', () {
......@@ -87,11 +87,11 @@ void main() {
i += 1;
}
expect(transformLayer.find<int>(const Offset(0.0, 100.0)), 0);
expect(transformLayer.find<int>(const Offset(0.0, 200.0)), 0);
expect(transformLayer.find<int>(const Offset(0.0, 270.0)), 1);
expect(transformLayer.find<int>(const Offset(0.0, 400.0)), 1);
expect(transformLayer.find<int>(const Offset(0.0, 530.0)), 2);
expect(transformLayer.find<int>(const Offset(0.0, 100.0)).annotation, 0);
expect(transformLayer.find<int>(const Offset(0.0, 200.0)).annotation, 0);
expect(transformLayer.find<int>(const Offset(0.0, 270.0)).annotation, 1);
expect(transformLayer.find<int>(const Offset(0.0, 400.0)).annotation, 1);
expect(transformLayer.find<int>(const Offset(0.0, 530.0)).annotation, 2);
});
test('looks for child AnnotatedRegions before parents', () {
......@@ -101,7 +101,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(parent.find<int>(Offset.zero), 2);
expect(parent.find<int>(Offset.zero).annotation, 2);
});
test('looks for correct type', () {
......@@ -111,7 +111,7 @@ void main() {
layer.append(child2);
layer.append(child1);
expect(layer.find<String>(Offset.zero), 'hello');
expect(layer.find<String>(Offset.zero).annotation, 'hello');
});
test('does not clip Layer.find on an AnnotatedRegion with an unrelated type', () {
......@@ -121,7 +121,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(layer.find<int>(const Offset(100.0, 100.0)), 1);
expect(layer.find<int>(const Offset(100.0, 100.0)).annotation, 1);
});
test('handles non-invertable transforms', () {
......@@ -133,7 +133,7 @@ void main() {
parent.transform = Matrix4.diagonal3Values(1.0, 1.0, 1.0);
expect(parent.find<int>(const Offset(0.0, 0.0)), 1);
expect(parent.find<int>(const Offset(0.0, 0.0)).annotation, 1);
});
});
group('$AnnotatedRegion findAll', () {
......@@ -151,9 +151,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)),equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)), equals(<int>[2]));
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
});
test('finds a value within the clip in a ClipRectLayer', () {
......@@ -170,9 +170,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)), equals(<int>[2]));
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
});
......@@ -190,9 +190,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(5.0, 5.0)), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(5.0, 105.0)), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(5.0, 205.0)), equals(<int>[2]));
expect(containerLayer.findAll<int>(const Offset(5.0, 5.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(5.0, 105.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(5.0, 205.0)).annotations.toList(), equals(<int>[2]));
});
test('finds a value under a TransformLayer', () {
......@@ -216,11 +216,11 @@ void main() {
i += 1;
}
expect(transformLayer.findAll<int>(const Offset(0.0, 100.0)), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 200.0)), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 270.0)), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 400.0)), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 530.0)), equals(<int>[2]));
expect(transformLayer.findAll<int>(const Offset(0.0, 100.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 200.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 270.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 400.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 530.0)).annotations.toList(), equals(<int>[2]));
});
test('finds multiple nested, overlapping regions', () {
......@@ -237,7 +237,7 @@ void main() {
parent.append(layer);
}
expect(parent.findAll<int>(const Offset(0.0, 0.0)), equals(<int>[3, 1, 2, 0,]));
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[3, 1, 2, 0,]));
});
test('looks for child AnnotatedRegions before parents', () {
......@@ -251,7 +251,7 @@ void main() {
parent.append(child3);
layer.append(parent);
expect(parent.findAll<int>(Offset.zero), equals(<int>[4, 3, 2, 1]));
expect(parent.findAll<int>(Offset.zero).annotations.toList(), equals(<int>[4, 3, 2, 1]));
});
test('looks for correct type', () {
......@@ -261,7 +261,7 @@ void main() {
layer.append(child2);
layer.append(child1);
expect(layer.findAll<String>(Offset.zero), equals(<String>['hello']));
expect(layer.findAll<String>(Offset.zero).annotations.toList(), equals(<String>['hello']));
});
test('does not clip Layer.find on an AnnotatedRegion with an unrelated type', () {
......@@ -271,7 +271,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(layer.findAll<int>(const Offset(100.0, 100.0)), equals(<int>[1]));
expect(layer.findAll<int>(const Offset(100.0, 100.0)).annotations.toList(), equals(<int>[1]));
});
test('handles non-invertable transforms', () {
......@@ -279,11 +279,11 @@ void main() {
final TransformLayer parent = TransformLayer(transform: Matrix4.diagonal3Values(0.0, 1.0, 1.0));
parent.append(child);
expect(parent.findAll<int>(const Offset(0.0, 0.0)), equals(<int>[]));
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[]));
parent.transform = Matrix4.diagonal3Values(1.0, 1.0, 1.0);
expect(parent.findAll<int>(const Offset(0.0, 0.0)), equals(<int>[1]));
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[1]));
});
});
}
This diff is collapsed.
......@@ -34,12 +34,12 @@ void main() {
int result = RendererBinding.instance.renderView.debugLayer.find<int>(Offset(
10.0 * window.devicePixelRatio,
10.0 * window.devicePixelRatio,
));
))?.annotation;
expect(result, null);
result = RendererBinding.instance.renderView.debugLayer.find<int>(Offset(
50.0 * window.devicePixelRatio,
50.0 * window.devicePixelRatio,
));
)).annotation;
expect(result, 1);
});
}
......@@ -752,6 +752,196 @@ void main() {
expect(paintCount, 1);
});
group('MouseRegion respects opacity:', () {
// A widget that contains 3 MouseRegions:
// y
// —————————————————————— 0
// | ——————————— A | 20
// | | B | |
// | | ——————————— | 50
// | | | C | |
// | ——————| | | 100
// | | | |
// | ——————————— | 130
// —————————————————————— 150
// x 0 20 50 100 130 150
Widget tripleRegions({bool opaqueC, void Function(String) addLog}) {
// Same as MouseRegion, but when opaque is null, use the default value.
Widget mouseRegionWithOptionalOpaque({
void Function(PointerEnterEvent e) onEnter,
void Function(PointerExitEvent e) onExit,
Widget child,
bool opaque,
}) {
if (opaque == null) {
return MouseRegion(onEnter: onEnter, onExit: onExit, child: child);
}
return MouseRegion(onEnter: onEnter, onExit: onExit, child: child, opaque: opaque);
}
return Directionality(
textDirection: TextDirection.ltr,
child: Align(
alignment: Alignment.topLeft,
child: MouseRegion(
onEnter: (PointerEnterEvent e) { addLog('enterA'); },
onExit: (PointerExitEvent e) { addLog('exitA'); },
child: SizedBox(
width: 150,
height: 150,
child: Stack(
children: <Widget>[
Positioned(
left: 20,
top: 20,
width: 80,
height: 80,
child: MouseRegion(
onEnter: (PointerEnterEvent e) { addLog('enterB'); },
onExit: (PointerExitEvent e) { addLog('exitB'); },
),
),
Positioned(
left: 50,
top: 50,
width: 80,
height: 80,
child: mouseRegionWithOptionalOpaque(
opaque: opaqueC,
onEnter: (PointerEnterEvent e) { addLog('enterC'); },
onExit: (PointerExitEvent e) { addLog('exitC'); },
),
),
],
),
),
),
),
);
}
testWidgets('a transparent one should allow MouseRegions behind it to receive pointers', (WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(tripleRegions(
opaqueC: false,
addLog: (String log) => logs.add(log),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await tester.pumpAndSettle();
// Move to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterC', 'enterB']);
logs.clear();
// Move to the B only area
await gesture.moveTo(const Offset(25, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitC']);
logs.clear();
// Move back to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterC']);
logs.clear();
// Move to the C only area
await gesture.moveTo(const Offset(125, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitB']);
logs.clear();
// Move back to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterB']);
logs.clear();
// Move out
await gesture.moveTo(const Offset(160, 160));
await tester.pumpAndSettle();
expect(logs, <String>['exitA', 'exitB', 'exitC']);
});
testWidgets('an opaque one should prevent MouseRegions behind it receiving pointers', (WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(tripleRegions(
opaqueC: true,
addLog: (String log) => logs.add(log),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await tester.pumpAndSettle();
// Move to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterC']);
logs.clear();
// Move to the B only area
await gesture.moveTo(const Offset(25, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterB', 'exitC']);
logs.clear();
// Move back to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterC', 'exitB']);
logs.clear();
// Move to the C only area
await gesture.moveTo(const Offset(125, 75));
await tester.pumpAndSettle();
expect(logs, <String>[]);
logs.clear();
// Move back to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>[]);
logs.clear();
// Move out
await gesture.moveTo(const Offset(160, 160));
await tester.pumpAndSettle();
expect(logs, <String>['exitA', 'exitC']);
});
testWidgets('opaque should default to true', (WidgetTester tester) async {
final List<String> logs = <String>[];
await tester.pumpWidget(tripleRegions(
opaqueC: null,
addLog: (String log) => logs.add(log),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await tester.pumpAndSettle();
// Move to the overlapping area
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterC']);
logs.clear();
// Move out
await gesture.moveTo(const Offset(160, 160));
await tester.pumpAndSettle();
expect(logs, <String>['exitA', 'exitC']);
});
});
testWidgets('RenderMouseRegion\'s debugFillProperties when default', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
RenderMouseRegion().debugFillProperties(builder);
......
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