Commit 2fc5a759 authored by Adam Barth's avatar Adam Barth

Add the ability to have translucent gesture detectors

A translucent gesture detector still listens for gestures but also lets the
content visually behind the detector receive events.
parent 7bafe54a
......@@ -977,6 +977,12 @@ class RenderCustomPaint extends RenderProxyBox {
typedef void PointerEventListener(PointerInputEvent e);
enum HitTestBehavior {
deferToChild,
opaque,
translucent,
}
/// Invokes the callbacks in response to pointer events.
class RenderPointerListener extends RenderProxyBox {
RenderPointerListener({
......@@ -984,6 +990,7 @@ class RenderPointerListener extends RenderProxyBox {
this.onPointerMove,
this.onPointerUp,
this.onPointerCancel,
this.behavior: HitTestBehavior.deferToChild,
RenderBox child
}) : super(child);
......@@ -991,6 +998,20 @@ class RenderPointerListener extends RenderProxyBox {
PointerEventListener onPointerMove;
PointerEventListener onPointerUp;
PointerEventListener onPointerCancel;
HitTestBehavior behavior;
bool hitTest(HitTestResult result, { Point position }) {
bool hitTarget = false;
if (position.x >= 0.0 && position.x < size.width &&
position.y >= 0.0 && position.y < size.height) {
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
if (hitTarget || behavior == HitTestBehavior.translucent)
result.add(new BoxHitTestEntry(this, position));
}
return hitTarget;
}
bool hitTestSelf(Point position) => behavior == HitTestBehavior.opaque;
void handleEvent(InputEvent event, HitTestEntry entry) {
if (onPointerDown != null && event.type == 'pointerdown')
......@@ -1017,6 +1038,8 @@ class RenderPointerListener extends RenderProxyBox {
if (listeners.isEmpty)
listeners.add('<none>');
settings.add('listeners: ${listeners.join(", ")}');
if (behavior != HitTestBehavior.deferToChild)
settings.add('behavior: $behavior');
}
}
......
......@@ -31,6 +31,7 @@ export 'package:flutter/rendering.dart' show
FontWeight,
FractionalOffset,
Gradient,
HitTestBehavior,
ImageFit,
ImageRepeat,
InputEvent,
......@@ -1229,19 +1230,24 @@ class Listener extends OneChildRenderObjectWidget {
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onPointerCancel
}) : super(key: key, child: child);
this.onPointerCancel,
this.behavior: HitTestBehavior.deferToChild
}) : super(key: key, child: child) {
assert(behavior != null);
}
final PointerEventListener onPointerDown;
final PointerEventListener onPointerMove;
final PointerEventListener onPointerUp;
final PointerEventListener onPointerCancel;
final HitTestBehavior behavior;
RenderPointerListener createRenderObject() => new RenderPointerListener(
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onPointerCancel: onPointerCancel
onPointerCancel: onPointerCancel,
behavior: behavior
);
void updateRenderObject(RenderPointerListener renderObject, Listener oldWidget) {
......@@ -1249,6 +1255,7 @@ class Listener extends OneChildRenderObjectWidget {
renderObject.onPointerMove = onPointerMove;
renderObject.onPointerUp = onPointerUp;
renderObject.onPointerCancel = onPointerCancel;
renderObject.behavior = behavior;
}
}
......
......@@ -48,7 +48,8 @@ class GestureDetector extends StatefulComponent {
this.onPanEnd,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd
this.onScaleEnd,
this.behavior
}) : super(key: key);
final Widget child;
......@@ -77,6 +78,8 @@ class GestureDetector extends StatefulComponent {
final GestureScaleUpdateCallback onScaleUpdate;
final GestureScaleEndCallback onScaleEnd;
final HitTestBehavior behavior;
_GestureDetectorState createState() => new _GestureDetectorState();
}
......@@ -224,9 +227,14 @@ class _GestureDetectorState extends State<GestureDetector> {
_scale.addPointer(event);
}
HitTestBehavior get _defaultBehavior {
return config.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
}
Widget build(BuildContext context) {
return new Listener(
onPointerDown: _handlePointerDown,
behavior: config.behavior ?? _defaultBehavior,
child: config.child
);
}
......
......@@ -134,4 +134,69 @@ void main() {
expect(didEndPan, isTrue);
});
});
test('Translucent', () {
testWidgets((WidgetTester tester) {
bool didReceivePointerDown;
bool didTap;
void pumpWidgetTree(HitTestBehavior behavior) {
tester.pumpWidget(
new Stack([
new Listener(
onPointerDown: (_) {
didReceivePointerDown = true;
},
child: new Container(
width: 100.0,
height: 100.0,
decoration: const BoxDecoration(
backgroundColor: const Color(0xFF00FF00)
)
)
),
new Container(
width: 100.0,
height: 100.0,
child: new GestureDetector(
onTap: () {
didTap = true;
},
behavior: behavior
)
)
])
);
}
didReceivePointerDown = false;
didTap = false;
pumpWidgetTree(null);
tester.tapAt(new Point(10.0, 10.0));
expect(didReceivePointerDown, isTrue);
expect(didTap, isTrue);
didReceivePointerDown = false;
didTap = false;
pumpWidgetTree(HitTestBehavior.deferToChild);
tester.tapAt(new Point(10.0, 10.0));
expect(didReceivePointerDown, isTrue);
expect(didTap, isFalse);
didReceivePointerDown = false;
didTap = false;
pumpWidgetTree(HitTestBehavior.opaque);
tester.tapAt(new Point(10.0, 10.0));
expect(didReceivePointerDown, isFalse);
expect(didTap, isTrue);
didReceivePointerDown = false;
didTap = false;
pumpWidgetTree(HitTestBehavior.translucent);
tester.tapAt(new Point(10.0, 10.0));
expect(didReceivePointerDown, isTrue);
expect(didTap, isTrue);
});
});
}
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