Commit 0df3730d authored by Hixie's avatar Hixie

Just-in-time mutations of GestureDetector

This allows us to adjust exactly which gestures we're listening for
during layout, which I'll use to kill a SizeObserver.
parent d1154c25
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.MaterialGallery" android:versionCode="1" android:versionName="0.0.1"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.MaterialGallery" android:versionCode="1" android:versionName="0.0.1">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET"/>
<application android:icon="@mipmap/ic_launcher" android:label="Flutter Material" android:name="org.domokit.sky.shell.SkyApplication"> <application android:icon="@mipmap/ic_launcher" android:label="Flutter Material" android:name="org.domokit.sky.shell.SkyApplication">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:name="org.domokit.sky.shell.SkyActivity" android:theme="@android:style/Theme.Black.NoTitleBar"> <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:name="org.domokit.sky.shell.SkyActivity" android:theme="@android:style/Theme.Black.NoTitleBar">
......
...@@ -143,6 +143,8 @@ class VerticalDragGestureRecognizer extends _DragGestureRecognizer<double> { ...@@ -143,6 +143,8 @@ class VerticalDragGestureRecognizer extends _DragGestureRecognizer<double> {
double get _initialPendingDragDelta => 0.0; double get _initialPendingDragDelta => 0.0;
double _getDragDelta(PointerEvent event) => event.delta.dy; double _getDragDelta(PointerEvent event) => event.delta.dy;
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop; bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop;
String toStringShort() => 'vertical drag';
} }
class HorizontalDragGestureRecognizer extends _DragGestureRecognizer<double> { class HorizontalDragGestureRecognizer extends _DragGestureRecognizer<double> {
...@@ -163,6 +165,8 @@ class HorizontalDragGestureRecognizer extends _DragGestureRecognizer<double> { ...@@ -163,6 +165,8 @@ class HorizontalDragGestureRecognizer extends _DragGestureRecognizer<double> {
double get _initialPendingDragDelta => 0.0; double get _initialPendingDragDelta => 0.0;
double _getDragDelta(PointerEvent event) => event.delta.dx; double _getDragDelta(PointerEvent event) => event.delta.dx;
bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop; bool get _hasSufficientPendingDragDeltaToAccept => _pendingDragDelta.abs() > kTouchSlop;
String toStringShort() => 'horizontal drag';
} }
class PanGestureRecognizer extends _DragGestureRecognizer<Offset> { class PanGestureRecognizer extends _DragGestureRecognizer<Offset> {
...@@ -185,4 +189,6 @@ class PanGestureRecognizer extends _DragGestureRecognizer<Offset> { ...@@ -185,4 +189,6 @@ class PanGestureRecognizer extends _DragGestureRecognizer<Offset> {
bool get _hasSufficientPendingDragDeltaToAccept { bool get _hasSufficientPendingDragDeltaToAccept {
return _pendingDragDelta.distance > kPanSlop; return _pendingDragDelta.distance > kPanSlop;
} }
String toStringShort() => 'pan';
} }
...@@ -34,4 +34,6 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer { ...@@ -34,4 +34,6 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
if (event is PointerUpEvent) if (event is PointerUpEvent)
resolve(GestureDisposition.rejected); resolve(GestureDisposition.rejected);
} }
String toStringShort() => 'long press';
} }
...@@ -250,6 +250,8 @@ class ImmediateMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Im ...@@ -250,6 +250,8 @@ class ImmediateMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Im
_ImmediatePointerState createNewPointerState(PointerDownEvent event) { _ImmediatePointerState createNewPointerState(PointerDownEvent event) {
return new _ImmediatePointerState(event.position); return new _ImmediatePointerState(event.position);
} }
String toStringShort() => 'multidrag';
} }
...@@ -277,6 +279,8 @@ class HorizontalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_H ...@@ -277,6 +279,8 @@ class HorizontalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_H
_HorizontalPointerState createNewPointerState(PointerDownEvent event) { _HorizontalPointerState createNewPointerState(PointerDownEvent event) {
return new _HorizontalPointerState(event.position); return new _HorizontalPointerState(event.position);
} }
String toStringShort() => 'horizontal multidrag';
} }
...@@ -304,6 +308,8 @@ class VerticalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Ver ...@@ -304,6 +308,8 @@ class VerticalMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Ver
_VerticalPointerState createNewPointerState(PointerDownEvent event) { _VerticalPointerState createNewPointerState(PointerDownEvent event) {
return new _VerticalPointerState(event.position); return new _VerticalPointerState(event.position);
} }
String toStringShort() => 'vertical multidrag';
} }
...@@ -373,4 +379,6 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Dela ...@@ -373,4 +379,6 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Dela
_DelayedPointerState createNewPointerState(PointerDownEvent event) { _DelayedPointerState createNewPointerState(PointerDownEvent event) {
return new _DelayedPointerState(event.position, _delay); return new _DelayedPointerState(event.position, _delay);
} }
String toStringShort() => 'long multidrag';
} }
...@@ -212,6 +212,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer { ...@@ -212,6 +212,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
} }
} }
String toStringShort() => 'double tap';
} }
...@@ -385,4 +386,5 @@ class MultiTapGestureRecognizer extends GestureRecognizer { ...@@ -385,4 +386,5 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
_gestureArena = null; _gestureArena = null;
} }
String toStringShort() => 'multitap';
} }
...@@ -42,6 +42,9 @@ abstract class GestureRecognizer extends GestureArenaMember { ...@@ -42,6 +42,9 @@ abstract class GestureRecognizer extends GestureArenaMember {
/// GestureDetector widget calls this method). /// GestureDetector widget calls this method).
void dispose() { } void dispose() { }
/// Returns a very short pretty description of the gesture that the
/// recognizer looks for, like 'tap' or 'horizontal drag'.
String toStringShort() => toString();
} }
/// Base class for gesture recognizers that can only recognize one /// Base class for gesture recognizers that can only recognize one
......
...@@ -135,4 +135,6 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -135,4 +135,6 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
} }
_state = ScaleState.ready; _state = ScaleState.ready;
} }
String toStringShort() => 'scale';
} }
...@@ -102,4 +102,6 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { ...@@ -102,4 +102,6 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
_wonArena = false; _wonArena = false;
_finalPosition = null; _finalPosition = null;
} }
String toStringShort() => 'tap';
} }
...@@ -28,6 +28,8 @@ export 'package:flutter/gestures.dart' show ...@@ -28,6 +28,8 @@ export 'package:flutter/gestures.dart' show
GestureScaleEndCallback, GestureScaleEndCallback,
Velocity; Velocity;
typedef GestureRecognizer GestureRecognizerFactory(GestureRecognizer recognizer, PointerRouter router, GestureArena arena);
/// A widget that detects gestures. /// A widget that detects gestures.
/// ///
/// Attempts to recognize gestures that correspond to its non-null callbacks. /// Attempts to recognize gestures that correspond to its non-null callbacks.
...@@ -37,8 +39,8 @@ export 'package:flutter/gestures.dart' show ...@@ -37,8 +39,8 @@ export 'package:flutter/gestures.dart' show
/// [excludeFromSemantics] to true. /// [excludeFromSemantics] to true.
/// ///
/// See http://flutter.io/gestures/ for additional information. /// See http://flutter.io/gestures/ for additional information.
class GestureDetector extends StatefulComponent { class GestureDetector extends StatelessComponent {
const GestureDetector({ GestureDetector({
Key key, Key key,
this.child, this.child,
this.onTapDown, this.onTapDown,
...@@ -61,7 +63,23 @@ class GestureDetector extends StatefulComponent { ...@@ -61,7 +63,23 @@ class GestureDetector extends StatefulComponent {
this.onScaleEnd, this.onScaleEnd,
this.behavior, this.behavior,
this.excludeFromSemantics: false this.excludeFromSemantics: false
}) : super(key: key); }) : super(key: key) {
assert(excludeFromSemantics != null);
assert(() {
bool haveVerticalDrag = onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null;
bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
if (havePan || haveScale) {
if (havePan && haveScale)
throw new WidgetError('Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer.');
String recognizer = havePan ? 'pan' : 'scale';
if (haveVerticalDrag && haveHorizontalDrag)
throw new WidgetError('Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.');
}
return true;
});
}
final Widget child; final Widget child;
...@@ -130,151 +148,192 @@ class GestureDetector extends StatefulComponent { ...@@ -130,151 +148,192 @@ class GestureDetector extends StatefulComponent {
/// duplication of information. /// duplication of information.
final bool excludeFromSemantics; final bool excludeFromSemantics;
_GestureDetectorState createState() => new _GestureDetectorState(); Widget build(BuildContext context) {
} Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
class _GestureDetectorState extends State<GestureDetector> { if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
PointerRouter get _router => Gesturer.instance.pointerRouter; gestures[TapGestureRecognizer] = (TapGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
return (recognizer ??= new TapGestureRecognizer(router: router, gestureArena: arena))
TapGestureRecognizer _tap; ..onTapDown = onTapDown
DoubleTapGestureRecognizer _doubleTap; ..onTapUp = onTapUp
LongPressGestureRecognizer _longPress; ..onTap = onTap
VerticalDragGestureRecognizer _verticalDrag; ..onTapCancel = onTapCancel;
HorizontalDragGestureRecognizer _horizontalDrag; };
PanGestureRecognizer _pan; }
ScaleGestureRecognizer _scale;
void initState() { if (onDoubleTap != null) {
super.initState(); gestures[DoubleTapGestureRecognizer] = (DoubleTapGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
_syncAll(); return (recognizer ??= new DoubleTapGestureRecognizer(router: router, gestureArena: arena))
} ..onDoubleTap = onDoubleTap;
};
}
void didUpdateConfig(GestureDetector oldConfig) { if (onLongPress != null) {
_syncAll(); gestures[LongPressGestureRecognizer] = (LongPressGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
} return (recognizer ??= new LongPressGestureRecognizer(router: router, gestureArena: arena))
..onLongPress = onLongPress;
};
}
void dispose() { if (onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null) {
_tap = _ensureDisposed(_tap); gestures[VerticalDragGestureRecognizer] = (VerticalDragGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
_doubleTap = _ensureDisposed(_doubleTap); return (recognizer ??= new VerticalDragGestureRecognizer(router: router, gestureArena: arena))
_longPress = _ensureDisposed(_longPress); ..onStart = onVerticalDragStart
_verticalDrag = _ensureDisposed(_verticalDrag); ..onUpdate = onVerticalDragUpdate
_horizontalDrag = _ensureDisposed(_horizontalDrag); ..onEnd = onVerticalDragEnd;
_pan = _ensureDisposed(_pan); };
_scale = _ensureDisposed(_scale); }
super.dispose();
}
void _syncAll() { if (onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null) {
_syncTap(); gestures[HorizontalDragGestureRecognizer] = (HorizontalDragGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
_syncDoubleTap(); return (recognizer ??= new HorizontalDragGestureRecognizer(router: router, gestureArena: arena))
_syncLongPress(); ..onStart = onHorizontalDragStart
_syncVerticalDrag(); ..onUpdate = onHorizontalDragUpdate
_syncHorizontalDrag(); ..onEnd = onHorizontalDragEnd;
_syncPan(); };
_syncScale(); }
}
void _syncTap() { if (onPanStart != null || onPanUpdate != null || onPanEnd != null) {
if (config.onTapDown == null && config.onTapUp == null && config.onTap == null && config.onTapCancel == null) { gestures[PanGestureRecognizer] = (PanGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
_tap = _ensureDisposed(_tap); return (recognizer ??= new PanGestureRecognizer(router: router, gestureArena: arena))
} else { ..onStart = onPanStart
_tap ??= new TapGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena); ..onUpdate = onPanUpdate
_tap ..onEnd = onPanEnd;
..onTapDown = config.onTapDown };
..onTapUp = config.onTapUp
..onTap = config.onTap
..onTapCancel = config.onTapCancel;
} }
}
void _syncDoubleTap() { if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
if (config.onDoubleTap == null) { gestures[ScaleGestureRecognizer] = (ScaleGestureRecognizer recognizer, PointerRouter router, GestureArena arena) {
_doubleTap = _ensureDisposed(_doubleTap); return (recognizer ??= new ScaleGestureRecognizer(router: router, gestureArena: arena))
} else { ..onStart = onScaleStart
_doubleTap ??= new DoubleTapGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena); ..onUpdate = onScaleUpdate
_doubleTap.onDoubleTap = config.onDoubleTap; ..onEnd = onScaleEnd;
};
} }
return new RawGestureDetector(
gestures: gestures,
behavior: behavior,
excludeFromSemantics: excludeFromSemantics,
child: child
);
} }
}
void _syncLongPress() { /// A widget that detects gestures described by the given gesture
if (config.onLongPress == null) { /// factories.
_longPress = _ensureDisposed(_longPress); ///
} else { /// For common gestures, use a [GestureRecognizer].
_longPress ??= new LongPressGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena); /// RawGestureDetector is useful primarily when developing your
_longPress.onLongPress = config.onLongPress; /// own gesture recognizers.
} class RawGestureDetector extends StatefulComponent {
RawGestureDetector({
Key key,
this.child,
this.gestures: const <Type, GestureRecognizerFactory>{},
this.behavior,
this.excludeFromSemantics: false
}) : super(key: key) {
assert(gestures != null);
assert(excludeFromSemantics != null);
} }
void _syncVerticalDrag() { final Widget child;
if (config.onVerticalDragStart == null && config.onVerticalDragUpdate == null && config.onVerticalDragEnd == null) {
_verticalDrag = _ensureDisposed(_verticalDrag); final Map<Type, GestureRecognizerFactory> gestures;
} else {
_verticalDrag ??= new VerticalDragGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena); /// How this gesture detector should behave during hit testing.
_verticalDrag final HitTestBehavior behavior;
..onStart = config.onVerticalDragStart
..onUpdate = config.onVerticalDragUpdate /// Whether to exclude these gestures from the semantics tree. For
..onEnd = config.onVerticalDragEnd; /// example, the long-press gesture for showing a tooltip is
} /// excluded because the tooltip itself is included in the semantics
/// tree directly and so having a gesture to show it would result in
/// duplication of information.
final bool excludeFromSemantics;
RawGestureDetectorState createState() => new RawGestureDetectorState();
}
class RawGestureDetectorState extends State<RawGestureDetector> {
Map<Type, GestureRecognizer> _recognizers = const <Type, GestureRecognizer>{};
void initState() {
super.initState();
_syncAll(config.gestures);
} }
void _syncHorizontalDrag() { void didUpdateConfig(RawGestureDetector oldConfig) {
if (config.onHorizontalDragStart == null && config.onHorizontalDragUpdate == null && config.onHorizontalDragEnd == null) { _syncAll(config.gestures);
_horizontalDrag = _ensureDisposed(_horizontalDrag);
} else {
_horizontalDrag ??= new HorizontalDragGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena);
_horizontalDrag
..onStart = config.onHorizontalDragStart
..onUpdate = config.onHorizontalDragUpdate
..onEnd = config.onHorizontalDragEnd;
}
} }
void _syncPan() { /// This method can be called after the build phase, during the
if (config.onPanStart == null && config.onPanUpdate == null && config.onPanEnd == null) { /// layout of the nearest descendant RenderObjectWidget of the
_pan = _ensureDisposed(_pan); /// gesture detector, to update the list of active gesture
} else { /// recognizers.
assert(_scale == null); // Scale is a superset of pan; just use scale ///
_pan ??= new PanGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena); /// The typical use case is [Scrollable]s, which put their viewport
_pan /// in their gesture detector, and then need to know the dimensions
..onStart = config.onPanStart /// of the viewport and the viewport's child to determine whether
..onUpdate = config.onPanUpdate /// the gesture detector should be enabled.
..onEnd = config.onPanEnd; void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
assert(() {
RenderObject renderObject = context.findRenderObject();
assert(renderObject is RenderPointerListener);
RenderPointerListener listener = renderObject;
RenderBox descendant = listener.child;
if (!config.excludeFromSemantics) {
assert(descendant is RenderSemanticsGestureHandler);
RenderSemanticsGestureHandler semanticsGestureHandler = descendant;
descendant = semanticsGestureHandler.child;
}
assert(descendant != null);
if (!descendant.debugDoingThisLayout) {
throw new WidgetError(
'replaceGestureRecognizers() can only be called during the layout phase of the GestureDetector\'s nearest descendant RenderObjectWidget.\n'
'In this particular case, that is:\n'
' $descendant'
);
}
return true;
});
_syncAll(gestures);
if (!config.excludeFromSemantics) {
RenderPointerListener listener = context.findRenderObject();
RenderSemanticsGestureHandler semanticsGestureHandler = listener.child;
context.visitChildElements((RenderObjectElement element) => element.widget.updateRenderObject(semanticsGestureHandler, null));
} }
} }
void _syncScale() { void dispose() {
if (config.onScaleStart == null && config.onScaleUpdate == null && config.onScaleEnd == null) { for (GestureRecognizer recognizer in _recognizers.values)
_scale = _ensureDisposed(_scale); recognizer.dispose();
} else { _recognizers = null;
assert(_pan == null); // Scale is a superset of pan; just use scale super.dispose();
_scale ??= new ScaleGestureRecognizer(router: _router, gestureArena: Gesturer.instance.gestureArena);
_scale
..onStart = config.onScaleStart
..onUpdate = config.onScaleUpdate
..onEnd = config.onScaleEnd;
}
} }
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) { void _syncAll(Map<Type, GestureRecognizerFactory> gestures) {
recognizer?.dispose(); final PointerRouter pointerRouter = Gesturer.instance.pointerRouter;
return null; final GestureArena gestureArena = Gesturer.instance.gestureArena;
assert(_recognizers != null);
Map<Type, GestureRecognizer> oldRecognizers = _recognizers;
_recognizers = <Type, GestureRecognizer>{};
for (Type type in gestures.keys) {
assert(!_recognizers.containsKey(type));
_recognizers[type] = gestures[type](oldRecognizers[type], pointerRouter, gestureArena);
assert(_recognizers[type].runtimeType == type);
}
for (Type type in oldRecognizers.keys) {
if (!_recognizers.containsKey(type))
oldRecognizers[type].dispose();
}
} }
void _handlePointerDown(PointerDownEvent event) { void _handlePointerDown(PointerDownEvent event) {
if (_tap != null) assert(_recognizers != null);
_tap.addPointer(event); for (GestureRecognizer recognizer in _recognizers.values)
if (_doubleTap != null) recognizer.addPointer(event);
_doubleTap.addPointer(event);
if (_longPress != null)
_longPress.addPointer(event);
if (_verticalDrag != null)
_verticalDrag.addPointer(event);
if (_horizontalDrag != null)
_horizontalDrag.addPointer(event);
if (_pan != null)
_pan.addPointer(event);
if (_scale != null)
_scale.addPointer(event);
} }
HitTestBehavior get _defaultBehavior { HitTestBehavior get _defaultBehavior {
...@@ -287,47 +346,21 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -287,47 +346,21 @@ class _GestureDetectorState extends State<GestureDetector> {
behavior: config.behavior ?? _defaultBehavior, behavior: config.behavior ?? _defaultBehavior,
child: config.child child: config.child
); );
if (!config.excludeFromSemantics) { if (!config.excludeFromSemantics)
result = new _GestureSemantics( result = new _GestureSemantics(owner: this, child: result);
onTapDown: config.onTapDown,
onTapUp: config.onTapUp,
onTap: config.onTap,
onLongPress: config.onLongPress,
onVerticalDragStart: config.onVerticalDragStart,
onVerticalDragUpdate: config.onVerticalDragUpdate,
onVerticalDragEnd: config.onVerticalDragEnd,
onHorizontalDragStart: config.onHorizontalDragStart,
onHorizontalDragUpdate: config.onHorizontalDragUpdate,
onHorizontalDragEnd: config.onHorizontalDragEnd,
onPanStart: config.onPanStart,
onPanUpdate: config.onPanUpdate,
onPanEnd: config.onPanEnd,
child: result
);
}
return result; return result;
} }
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
List<String> gestures = <String>[]; if (_recognizers == null) {
if (_tap != null) description.add('DISPOSED');
gestures.add('tap'); } else {
if (_doubleTap != null) List<String> gestures = _recognizers.values.map/*<String>*/((GestureRecognizer recognizer) => recognizer.toStringShort());
gestures.add('double tap'); if (gestures.isEmpty)
if (_longPress != null) gestures.add('<none>');
gestures.add('long press'); description.add('gestures: ${gestures.join(", ")}');
if (_verticalDrag != null) }
gestures.add('vertical drag');
if (_horizontalDrag != null)
gestures.add('horizontal drag');
if (_pan != null)
gestures.add('pan');
if (_scale != null)
gestures.add('scale');
if (gestures.isEmpty)
gestures.add('<none>');
description.add('gestures: ${gestures.join(", ")}');
switch (config.behavior) { switch (config.behavior) {
case HitTestBehavior.translucent: case HitTestBehavior.translucent:
description.add('behavior: translucent'); description.add('behavior: translucent');
...@@ -345,118 +378,99 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -345,118 +378,99 @@ class _GestureDetectorState extends State<GestureDetector> {
class _GestureSemantics extends OneChildRenderObjectWidget { class _GestureSemantics extends OneChildRenderObjectWidget {
_GestureSemantics({ _GestureSemantics({
Key key, Key key,
this.onTapDown, Widget child,
this.onTapUp, this.owner
this.onTap,
this.onLongPress,
this.onVerticalDragStart,
this.onVerticalDragUpdate,
this.onVerticalDragEnd,
this.onHorizontalDragStart,
this.onHorizontalDragUpdate,
this.onHorizontalDragEnd,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
Widget child
}) : super(key: key, child: child); }) : super(key: key, child: child);
final GestureTapDownCallback onTapDown; final RawGestureDetectorState owner;
final GestureTapUpCallback onTapUp;
final GestureTapCallback onTap;
final GestureLongPressCallback onLongPress;
final GestureDragStartCallback onVerticalDragStart;
final GestureDragUpdateCallback onVerticalDragUpdate;
final GestureDragEndCallback onVerticalDragEnd;
final GestureDragStartCallback onHorizontalDragStart;
final GestureDragUpdateCallback onHorizontalDragUpdate;
final GestureDragEndCallback onHorizontalDragEnd;
final GesturePanStartCallback onPanStart;
final GesturePanUpdateCallback onPanUpdate;
final GesturePanEndCallback onPanEnd;
bool get _watchingTaps {
return onTapDown != null
|| onTapUp != null
|| onTap != null;
}
bool get _watchingHorizontalDrags {
return onHorizontalDragStart != null
|| onHorizontalDragUpdate != null
|| onHorizontalDragEnd != null;
}
bool get _watchingVerticalDrags { void _handleTap() {
return onVerticalDragStart != null TapGestureRecognizer recognizer = owner._recognizers[TapGestureRecognizer];
|| onVerticalDragUpdate != null assert(recognizer != null);
|| onVerticalDragEnd != null; if (recognizer.onTapDown != null)
} recognizer.onTapDown(Point.origin);
if (recognizer.onTapUp != null)
bool get _watchingPans { recognizer.onTapUp(Point.origin);
return onPanStart != null if (recognizer.onTap != null)
|| onPanUpdate != null recognizer.onTap();
|| onPanEnd != null;
} }
void _handleTap() { void _handleLongPress() {
if (onTapDown != null) LongPressGestureRecognizer recognizer = owner._recognizers[LongPressGestureRecognizer];
onTapDown(Point.origin); assert(recognizer != null);
if (onTapUp != null) if (recognizer.onLongPress != null)
onTapUp(Point.origin); recognizer.onLongPress();
if (onTap != null)
onTap();
} }
void _handleHorizontalDragUpdate(double delta) { void _handleHorizontalDragUpdate(double delta) {
if (_watchingHorizontalDrags) { {
if (onHorizontalDragStart != null) HorizontalDragGestureRecognizer recognizer = owner._recognizers[HorizontalDragGestureRecognizer];
onHorizontalDragStart(Point.origin); if (recognizer != null) {
if (onHorizontalDragUpdate != null) if (recognizer.onStart != null)
onHorizontalDragUpdate(delta); recognizer.onStart(Point.origin);
if (onHorizontalDragEnd != null) if (recognizer.onUpdate != null)
onHorizontalDragEnd(Velocity.zero); recognizer.onUpdate(delta);
} else { if (recognizer.onEnd != null)
assert(_watchingPans); recognizer.onEnd(Velocity.zero);
if (onPanStart != null) return;
onPanStart(Point.origin); }
if (onPanUpdate != null) }
onPanUpdate(new Offset(delta, 0.0)); {
if (onPanEnd != null) PanGestureRecognizer recognizer = owner._recognizers[PanGestureRecognizer];
onPanEnd(Velocity.zero); if (recognizer != null) {
if (recognizer.onStart != null)
recognizer.onStart(Point.origin);
if (recognizer.onUpdate != null)
recognizer.onUpdate(new Offset(delta, 0.0));
if (recognizer.onEnd != null)
recognizer.onEnd(Velocity.zero);
return;
}
} }
assert(false);
} }
void _handleVerticalDragUpdate(double delta) { void _handleVerticalDragUpdate(double delta) {
if (_watchingVerticalDrags) { {
if (onVerticalDragStart != null) VerticalDragGestureRecognizer recognizer = owner._recognizers[VerticalDragGestureRecognizer];
onVerticalDragStart(Point.origin); if (recognizer != null) {
if (onVerticalDragUpdate != null) if (recognizer.onStart != null)
onVerticalDragUpdate(delta); recognizer.onStart(Point.origin);
if (onVerticalDragEnd != null) if (recognizer.onUpdate != null)
onVerticalDragEnd(Velocity.zero); recognizer.onUpdate(delta);
} else { if (recognizer.onEnd != null)
assert(_watchingPans); recognizer.onEnd(Velocity.zero);
if (onPanStart != null) return;
onPanStart(Point.origin); }
if (onPanUpdate != null)
onPanUpdate(new Offset(0.0, delta));
if (onPanEnd != null)
onPanEnd(Velocity.zero);
} }
{
PanGestureRecognizer recognizer = owner._recognizers[PanGestureRecognizer];
if (recognizer != null) {
if (recognizer.onStart != null)
recognizer.onStart(Point.origin);
if (recognizer.onUpdate != null)
recognizer.onUpdate(new Offset(0.0, delta));
if (recognizer.onEnd != null)
recognizer.onEnd(Velocity.zero);
return;
}
}
assert(false);
} }
RenderSemanticsGestureHandler createRenderObject() => new RenderSemanticsGestureHandler( RenderSemanticsGestureHandler createRenderObject() {
onTap: _watchingTaps ? _handleTap : null, RenderSemanticsGestureHandler result = new RenderSemanticsGestureHandler();
onLongPress: onLongPress, updateRenderObject(result, null);
onHorizontalDragUpdate: _watchingHorizontalDrags || _watchingPans ? _handleHorizontalDragUpdate : null, return result;
onVerticalDragUpdate: _watchingVerticalDrags || _watchingPans ? _handleVerticalDragUpdate : null }
);
void updateRenderObject(RenderSemanticsGestureHandler renderObject, _GestureSemantics oldWidget) { void updateRenderObject(RenderSemanticsGestureHandler renderObject, _GestureSemantics oldWidget) {
renderObject.onTap = _watchingTaps ? _handleTap : null; Map<Type, GestureRecognizer> recognizers = owner._recognizers;
renderObject.onLongPress = onLongPress; renderObject.onTap = recognizers.containsKey(TapGestureRecognizer) ? _handleTap : null;
renderObject.onHorizontalDragUpdate = _watchingHorizontalDrags || _watchingPans ? _handleHorizontalDragUpdate : null; renderObject.onLongPress = recognizers.containsKey(LongPressGestureRecognizer) ? _handleLongPress : null;
renderObject.onVerticalDragUpdate = _watchingVerticalDrags || _watchingPans ? _handleVerticalDragUpdate : null; renderObject.onHorizontalDragUpdate = recognizers.containsKey(VerticalDragGestureRecognizer) ||
recognizers.containsKey(PanGestureRecognizer) ? _handleHorizontalDragUpdate : null;
renderObject.onVerticalDragUpdate = recognizers.containsKey(VerticalDragGestureRecognizer) ||
recognizers.containsKey(PanGestureRecognizer) ? _handleVerticalDragUpdate : null;
} }
} }
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