using System; using System.Collections.Generic; using System.Linq; using Unity.UIWidgets.external; using Unity.UIWidgets.foundation; using Unity.UIWidgets.gestures; using Unity.UIWidgets.rendering; namespace Unity.UIWidgets.widgets { public abstract class GestureRecognizerFactory { public abstract GestureRecognizer constructorRaw(); public abstract void initializerRaw(GestureRecognizer instance); internal abstract bool _debugAssertTypeMatches(Type type); } public abstract class GestureRecognizerFactory : GestureRecognizerFactory where T : GestureRecognizer { public override GestureRecognizer constructorRaw() { return constructor(); } public override void initializerRaw(GestureRecognizer instance) { initializer((T) instance); } public abstract T constructor(); public abstract void initializer(T instance); internal override bool _debugAssertTypeMatches(Type type) { D.assert(type == typeof(T), () => "GestureRecognizerFactory of type " + typeof(T) + $" was used where type {type} was specified."); return true; } } public delegate T GestureRecognizerFactoryConstructor() where T : GestureRecognizer; public delegate void GestureRecognizerFactoryInitializer(T instance) where T : GestureRecognizer; public class GestureRecognizerFactoryWithHandlers : GestureRecognizerFactory where T : GestureRecognizer { public GestureRecognizerFactoryWithHandlers( GestureRecognizerFactoryConstructor constructor, GestureRecognizerFactoryInitializer initializer) { D.assert(constructor != null); D.assert(initializer != null); _constructor = constructor; _initializer = initializer; } readonly GestureRecognizerFactoryConstructor _constructor; readonly GestureRecognizerFactoryInitializer _initializer; public override T constructor() { return _constructor(); } public override void initializer(T instance) { _initializer(instance); } } public class GestureDetector : StatelessWidget { public GestureDetector( Key key = null, Widget child = null, GestureTapDownCallback onTapDown = null, GestureTapUpCallback onTapUp = null, GestureTapCallback onTap = null, GestureTapCancelCallback onTapCancel = null, GestureTapDownCallback onSecondaryTapDown = null, GestureTapUpCallback onSecondaryTapUp = null, GestureTapCancelCallback onSecondaryTapCancel = null, GestureDoubleTapCallback onDoubleTap = null, GestureLongPressCallback onLongPress = null, GestureLongPressStartCallback onLongPressStart = null, GestureLongPressMoveUpdateCallback onLongPressMoveUpdate = null, GestureLongPressUpCallback onLongPressUp = null, GestureLongPressEndCallback onLongPressEnd = null, GestureDragDownCallback onVerticalDragDown = null, GestureDragStartCallback onVerticalDragStart = null, GestureDragUpdateCallback onVerticalDragUpdate = null, GestureDragEndCallback onVerticalDragEnd = null, GestureDragCancelCallback onVerticalDragCancel = null, GestureDragDownCallback onHorizontalDragDown = null, GestureDragStartCallback onHorizontalDragStart = null, GestureDragUpdateCallback onHorizontalDragUpdate = null, GestureDragEndCallback onHorizontalDragEnd = null, GestureDragCancelCallback onHorizontalDragCancel = null, GestureForcePressStartCallback onForcePressStart = null, GestureForcePressPeakCallback onForcePressPeak = null, GestureForcePressUpdateCallback onForcePressUpdate = null, GestureForcePressEndCallback onForcePressEnd = null, GestureDragDownCallback onPanDown = null, GestureDragStartCallback onPanStart = null, GestureDragUpdateCallback onPanUpdate = null, GestureDragEndCallback onPanEnd = null, GestureDragCancelCallback onPanCancel = null, GestureScaleStartCallback onScaleStart = null, GestureScaleUpdateCallback onScaleUpdate = null, GestureScaleEndCallback onScaleEnd = null, HitTestBehavior behavior = HitTestBehavior.deferToChild, DragStartBehavior dragStartBehavior = DragStartBehavior.down ) : base(key) { D.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 UIWidgetsError(new List{ new ErrorSummary("Incorrect GestureDetector arguments."), new ErrorDescription( "Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan." ), new ErrorHint("Just use the scale gesture recognizer.") }); } string recognizer = havePan ? "pan" : "scale"; if (haveVerticalDrag && haveHorizontalDrag) { throw new UIWidgetsError( "Incorrect GestureDetector arguments.\n" + $"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; }); this.child = child; this.onTapDown = onTapDown; this.onTapUp = onTapUp; this.onTap = onTap; this.onTapCancel = onTapCancel; this.onSecondaryTapDown = onSecondaryTapDown; this.onSecondaryTapUp = onSecondaryTapUp; this.onSecondaryTapCancel = onSecondaryTapCancel; this.onDoubleTap = onDoubleTap; this.onLongPress = onLongPress; this.onLongPressUp = onLongPressUp; this.onLongPressStart = onLongPressStart; this.onLongPressMoveUpdate = onLongPressMoveUpdate; this.onLongPressEnd = onLongPressEnd; this.onVerticalDragDown = onVerticalDragDown; this.onVerticalDragStart = onVerticalDragStart; this.onVerticalDragUpdate = onVerticalDragUpdate; this.onVerticalDragEnd = onVerticalDragEnd; this.onVerticalDragCancel = onVerticalDragCancel; this.onHorizontalDragDown = onHorizontalDragDown; this.onHorizontalDragStart = onHorizontalDragStart; this.onHorizontalDragUpdate = onHorizontalDragUpdate; this.onHorizontalDragEnd = onHorizontalDragEnd; this.onHorizontalDragCancel = onHorizontalDragCancel; this.onForcePressEnd = onForcePressEnd; this.onForcePressPeak = onForcePressPeak; this.onForcePressStart = onForcePressStart; this.onForcePressUpdate = onForcePressUpdate; this.onPanDown = onPanDown; this.onPanStart = onPanStart; this.onPanUpdate = onPanUpdate; this.onPanEnd = onPanEnd; this.onPanCancel = onPanCancel; this.onScaleStart = onScaleStart; this.onScaleUpdate = onScaleUpdate; this.onScaleEnd = onScaleEnd; this.behavior = behavior; this.dragStartBehavior = dragStartBehavior; } public readonly Widget child; public readonly GestureTapDownCallback onTapDown; public readonly GestureTapUpCallback onTapUp; public readonly GestureTapCallback onTap; public readonly GestureTapCancelCallback onTapCancel; public readonly GestureTapDownCallback onSecondaryTapDown; public readonly GestureTapUpCallback onSecondaryTapUp; public readonly GestureTapCancelCallback onSecondaryTapCancel; public readonly GestureDoubleTapCallback onDoubleTap; public readonly GestureLongPressCallback onLongPress; public readonly GestureLongPressUpCallback onLongPressUp; public readonly GestureLongPressStartCallback onLongPressStart; public readonly GestureLongPressMoveUpdateCallback onLongPressMoveUpdate; public readonly GestureLongPressEndCallback onLongPressEnd; public readonly GestureDragDownCallback onVerticalDragDown; public readonly GestureDragStartCallback onVerticalDragStart; public readonly GestureDragUpdateCallback onVerticalDragUpdate; public readonly GestureDragEndCallback onVerticalDragEnd; public readonly GestureDragCancelCallback onVerticalDragCancel; public readonly GestureDragDownCallback onHorizontalDragDown; public readonly GestureDragStartCallback onHorizontalDragStart; public readonly GestureDragUpdateCallback onHorizontalDragUpdate; public readonly GestureDragEndCallback onHorizontalDragEnd; public readonly GestureDragCancelCallback onHorizontalDragCancel; public readonly GestureDragDownCallback onPanDown; public readonly GestureDragStartCallback onPanStart; public readonly GestureDragUpdateCallback onPanUpdate; public readonly GestureDragEndCallback onPanEnd; public readonly GestureDragCancelCallback onPanCancel; public readonly GestureScaleStartCallback onScaleStart; public readonly GestureScaleUpdateCallback onScaleUpdate; public readonly GestureScaleEndCallback onScaleEnd; public readonly HitTestBehavior behavior; public readonly DragStartBehavior dragStartBehavior; public readonly GestureForcePressStartCallback onForcePressStart; public readonly GestureForcePressPeakCallback onForcePressPeak; public readonly GestureForcePressUpdateCallback onForcePressUpdate; public readonly GestureForcePressEndCallback onForcePressEnd; public override Widget build(BuildContext context) { var gestures = new Dictionary(); if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null || onSecondaryTapDown != null || onSecondaryTapUp != null || onSecondaryTapCancel != null) { gestures[typeof(TapGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new TapGestureRecognizer(debugOwner: this), instance => { instance.onTapDown = onTapDown; instance.onTapUp = onTapUp; instance.onTap = onTap; instance.onTapCancel = onTapCancel; instance.onSecondaryTapDown = onSecondaryTapDown; instance.onSecondaryTapUp = onSecondaryTapUp; instance.onSecondaryTapCancel = onSecondaryTapCancel; } ); } if (onDoubleTap != null) { gestures[typeof(DoubleTapGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new DoubleTapGestureRecognizer(debugOwner: this), instance => { instance.onDoubleTap = onDoubleTap; } ); } if (onLongPress != null || onLongPressUp != null || onLongPressStart != null || onLongPressMoveUpdate != null || onLongPressEnd != null) { gestures[typeof(LongPressGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new LongPressGestureRecognizer(debugOwner: this), instance => { instance.onLongPress = onLongPress; instance.onLongPressStart = onLongPressStart; instance.onLongPressMoveUpdate = onLongPressMoveUpdate; instance.onLongPressEnd = onLongPressEnd; instance.onLongPressUp = onLongPressUp; } ); } if (onVerticalDragDown != null || onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null || onVerticalDragCancel != null) { gestures[typeof(VerticalDragGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new VerticalDragGestureRecognizer(debugOwner: this), instance => { instance.onDown = onVerticalDragDown; instance.onStart = onVerticalDragStart; instance.onUpdate = onVerticalDragUpdate; instance.onEnd = onVerticalDragEnd; instance.onCancel = onVerticalDragCancel; instance.dragStartBehavior = dragStartBehavior; } ); } if (onHorizontalDragDown != null || onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null || onHorizontalDragCancel != null) { gestures[typeof(HorizontalDragGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new HorizontalDragGestureRecognizer(debugOwner: this), instance => { instance.onDown = onHorizontalDragDown; instance.onStart = onHorizontalDragStart; instance.onUpdate = onHorizontalDragUpdate; instance.onEnd = onHorizontalDragEnd; instance.onCancel = onHorizontalDragCancel; instance.dragStartBehavior = dragStartBehavior; } ); } if (onPanDown != null || onPanStart != null || onPanUpdate != null || onPanEnd != null || onPanCancel != null) { gestures[typeof(PanGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new PanGestureRecognizer(debugOwner: this), instance => { instance.onDown = onPanDown; instance.onStart = onPanStart; instance.onUpdate = onPanUpdate; instance.onEnd = onPanEnd; instance.onCancel = onPanCancel; instance.dragStartBehavior = dragStartBehavior; } ); } if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) { gestures[typeof(ScaleGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new ScaleGestureRecognizer(debugOwner: this), instance => { instance.onStart = onScaleStart; instance.onUpdate = onScaleUpdate; instance.onEnd = onScaleEnd; } ); } if (onForcePressStart != null || onForcePressPeak != null || onForcePressUpdate != null || onForcePressEnd != null) { gestures[typeof(ForcePressGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers( () => new ForcePressGestureRecognizer(debugOwner: this), (ForcePressGestureRecognizer instance) => { instance.onStart = onForcePressStart; instance.onPeak = onForcePressPeak; instance.onUpdate = onForcePressUpdate; instance.onEnd = onForcePressEnd; } ); } return new RawGestureDetector( gestures: gestures, behavior: behavior, child: child ); } public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { base.debugFillProperties(properties); properties.add(new EnumProperty("startBehavior", dragStartBehavior)); } } public class RawGestureDetector : StatefulWidget { public RawGestureDetector( Key key = null, Widget child = null, Dictionary gestures = null, HitTestBehavior? behavior = null ) : base(key: key) { D.assert(gestures != null); this.child = child; this.gestures = gestures ?? new Dictionary(); this.behavior = behavior; } public readonly Widget child; public readonly Dictionary gestures; public readonly HitTestBehavior? behavior; public override State createState() { return new RawGestureDetectorState(); } } public class RawGestureDetectorState : State { Dictionary _recognizers = new Dictionary(); public override void initState() { base.initState(); _syncAll(widget.gestures); } public override void didUpdateWidget(StatefulWidget oldWidget) { base.didUpdateWidget(oldWidget); _syncAll(widget.gestures); } public void replaceGestureRecognizers(Dictionary gestures) { D.assert(() => { if (!context.findRenderObject().owner.debugDoingLayout) { throw new UIWidgetsError(new List { new ErrorSummary( "Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState."), new ErrorDescription( "The replaceGestureRecognizers() method can only be called during the layout phase."), new ErrorHint( "To set the gesture recognizers at other times, trigger a new build using setState() " + "and provide the new gesture recognizers as constructor arguments to the corresponding " + "RawGestureDetector or GestureDetector object." ) }); } return true; }); _syncAll(gestures); } public override void dispose() { foreach (GestureRecognizer recognizer in _recognizers.Values) { recognizer.dispose(); } _recognizers = null; base.dispose(); } void _syncAll(Dictionary gestures) { D.assert(_recognizers != null); var oldRecognizers = _recognizers; _recognizers = new Dictionary(); foreach (Type type in gestures.Keys) { D.assert(gestures[type] != null); D.assert(gestures[type]._debugAssertTypeMatches(type)); D.assert(!_recognizers.ContainsKey(type)); _recognizers[type] = oldRecognizers.ContainsKey(type) ? oldRecognizers[type] : gestures[type].constructorRaw(); D.assert(_recognizers[type].GetType() == type, () => "GestureRecognizerFactory of type " + type + " created a GestureRecognizer of type " + _recognizers[type].GetType() + ". The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method."); gestures[type].initializerRaw(_recognizers[type]); } foreach (Type type in oldRecognizers.Keys) { if (!_recognizers.ContainsKey(type)) { oldRecognizers[type].dispose(); } } } void _handlePointerDown(PointerDownEvent evt) { D.assert(_recognizers != null); foreach (GestureRecognizer recognizer in _recognizers.Values) { recognizer.addPointer(evt); } } void _handlePointerScroll(PointerScrollEvent evt) { D.assert(_recognizers != null); foreach (GestureRecognizer recognizer in _recognizers.Values) { recognizer.addScrollPointer(evt); } } HitTestBehavior _defaultBehavior { get { return widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild; } } public override Widget build(BuildContext context) { Widget result = new Listener( onPointerDown: _handlePointerDown, behavior: widget.behavior ?? _defaultBehavior, child: widget.child ); return result; } public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { base.debugFillProperties(properties); if (_recognizers == null) { properties.add(DiagnosticsNode.message("DISPOSED")); } else { List gestures = LinqUtils.SelectList(_recognizers.Values, (recognizer => recognizer.debugDescription)); properties.add(new EnumerableProperty("gestures", gestures, ifEmpty: "")); properties.add(new EnumerableProperty("recognizers", _recognizers.Values, level: DiagnosticLevel.fine)); } properties.add(new EnumProperty("behavior", widget.behavior, defaultValue: foundation_.kNullDefaultValue)); } } }