summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Tizen/GestureDetector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.Tizen/GestureDetector.cs')
-rw-r--r--Xamarin.Forms.Platform.Tizen/GestureDetector.cs581
1 files changed, 581 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/GestureDetector.cs b/Xamarin.Forms.Platform.Tizen/GestureDetector.cs
new file mode 100644
index 00000000..f2a3aac9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/GestureDetector.cs
@@ -0,0 +1,581 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Reflection;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using EGestureType = ElmSharp.GestureLayer.GestureType;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class GestureDetector
+ {
+ IDictionary<EGestureType, List<GestureHandler>> _handlerCache = new Dictionary<EGestureType, List<GestureHandler>>();
+
+ readonly IVisualElementRenderer _renderer;
+ GestureLayer _gestureLayer;
+ Polygon _hitBox;
+ double _doubleTapTime = 0;
+ double _longTapTime = 0;
+ int _horizontalSwipeTime = 0;
+ int _verticalSwipeTime = 0;
+
+ View View => _renderer.Element as View;
+
+ public GestureDetector(IVisualElementRenderer renderer)
+ {
+ _renderer = renderer;
+
+ (View.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged += OnGestureRecognizerCollectionChanged;
+
+ if (View.GestureRecognizers.Count > 0)
+ {
+ CreateGestureLayer();
+ AddGestures(View.GestureRecognizers);
+ }
+ }
+
+ public void Clear()
+ {
+ // this will clear all callbacks in ElmSharp GestureLayer
+ _gestureLayer.Unrealize();
+ foreach (var handlers in _handlerCache.Values)
+ {
+ foreach (var handler in handlers)
+ {
+ (handler as GestureHandler).PropertyChanged -= OnGestureRecognizerPropertyChanged;
+ }
+ }
+ _handlerCache.Clear();
+
+ (View.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged -= OnGestureRecognizerCollectionChanged;
+ if (_hitBox != null)
+ {
+ _hitBox.Unrealize();
+ _hitBox = null;
+ }
+ }
+
+ public void UpdateHitBox()
+ {
+ if (_hitBox == null)
+ return;
+ // _hitBox has to be used because gestures do not work well with transformations (EvasMap)
+ // so we create additional object which has the same shape as tranformed target, but does not have EvasMap on it
+ EvasObject target = _renderer.NativeView;
+ _hitBox.ClearPoints();
+ if (target.IsMapEnabled)
+ {
+ var map = target.EvasMap;
+ Point3D point;
+ for (var i = 0; i < 4; i++)
+ {
+ point = map.GetPointCoordinate(i);
+ _hitBox.AddPoint(point.X, point.Y);
+ }
+ }
+ else
+ {
+ var geometry = target.Geometry;
+ if (geometry.Width == 0 || geometry.Height == 0)
+ return;
+ _hitBox.AddPoint(geometry.Left, geometry.Top);
+ _hitBox.AddPoint(geometry.Right, geometry.Top);
+ _hitBox.AddPoint(geometry.Right, geometry.Bottom);
+ _hitBox.AddPoint(geometry.Left, geometry.Bottom);
+ }
+ }
+
+ void CreateGestureLayer()
+ {
+ _gestureLayer = new GestureLayer(_renderer.NativeView);
+ _gestureLayer.Attach(_renderer.NativeView);
+ }
+
+ void AddGestures(IEnumerable<IGestureRecognizer> recognizers)
+ {
+ foreach (var item in recognizers)
+ AddGesture(item);
+ }
+
+ void RemoveGestures(IEnumerable<IGestureRecognizer> recognizers)
+ {
+ foreach (var item in recognizers)
+ RemoveGesture(item);
+ }
+
+ void AddGesture(IGestureRecognizer recognizer)
+ {
+ var handler = CreateHandler(recognizer);
+ if (handler == null)
+ return;
+
+ var gestureType = handler.Type;
+ var timeout = handler.Timeout;
+ var cache = _handlerCache;
+
+ if (!cache.ContainsKey(gestureType))
+ {
+ cache[gestureType] = new List<GestureHandler>();
+ }
+
+ handler.PropertyChanged += OnGestureRecognizerPropertyChanged;
+ cache[gestureType].Add(handler);
+
+ if (cache[gestureType].Count == 1)
+ {
+ switch (gestureType)
+ {
+ case EGestureType.Tap:
+ AddTapGesture(gestureType);
+ break;
+
+ case EGestureType.DoubleTap:
+ AddDoubleTapGesture(gestureType, timeout);
+ break;
+
+ case EGestureType.TripleTap:
+ AddTapGesture(gestureType);
+ break;
+
+ case EGestureType.LongTap:
+ AddLongTapGesture(gestureType, timeout);
+ break;
+
+ case EGestureType.Line:
+ AddPanGesture(gestureType);
+ break;
+
+ case EGestureType.Flick:
+ AddFlickGesture(gestureType, timeout);
+ break;
+
+ case EGestureType.Rotate:
+ AddRotateGesture(gestureType);
+ break;
+
+ case EGestureType.Momentum:
+ AddMomentumGesture(gestureType);
+ break;
+
+ case EGestureType.Zoom:
+ AddPinchGesture(gestureType);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void RemoveGesture(IGestureRecognizer recognizer)
+ {
+ var cache = _handlerCache;
+ var handler = LookupHandler(recognizer);
+ var gestureType = cache.FirstOrDefault(x => x.Value.Contains(handler)).Key;
+
+ handler.PropertyChanged -= OnGestureRecognizerPropertyChanged;
+ cache[gestureType].Remove(handler);
+
+ if (cache[gestureType].Count == 0)
+ {
+ switch (gestureType)
+ {
+ case EGestureType.Tap:
+ case EGestureType.DoubleTap:
+ case EGestureType.TripleTap:
+ case EGestureType.LongTap:
+ RemoveTapGesture(gestureType);
+ break;
+
+ case EGestureType.Line:
+ RemovePanGesture();
+ break;
+
+ case EGestureType.Flick:
+ RemoveFlickGesture();
+ break;
+
+ case EGestureType.Rotate:
+ RemoveRotateGesture();
+ break;
+
+ case EGestureType.Momentum:
+ RemoveMomentumGesture();
+ break;
+
+ case EGestureType.Zoom:
+ RemovePinchGesture();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void EnsureHitBoxExists()
+ {
+ if (_hitBox == null)
+ {
+ Box parent = (Platform.GetRenderer(_renderer.Element.RealParent) as LayoutRenderer).Control;
+ _hitBox = new Polygon(parent)
+ {
+ Color = EColor.Transparent
+ };
+ _hitBox.Show();
+ UpdateHitBox();
+ parent.PackAfter(_hitBox, _renderer.NativeView);
+ _gestureLayer.Attach(_hitBox);
+ }
+ }
+
+ void AddPanGesture(EGestureType type)
+ {
+ EnsureHitBoxExists();
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, (data) => { OnGestureMoved(type, data); });
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddPinchGesture(EGestureType type)
+ {
+ EnsureHitBoxExists();
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, (data) => { OnGestureMoved(type, data); });
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddTapGesture(EGestureType type)
+ {
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddDoubleTapGesture(EGestureType type, double timeout)
+ {
+ if (timeout > 0)
+ _gestureLayer.DoubleTapTimeout = timeout;
+
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Start, (data) => { OnDoubleTapStarted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, (data) => { OnDoubleTapCompleted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddLongTapGesture(EGestureType type, double timeout)
+ {
+ if (timeout > 0)
+ _gestureLayer.LongTapTimeout = timeout;
+
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Start, (data) => { OnLongTapStarted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, (data) => { OnLongTapCompleted(type, data); });
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddFlickGesture(EGestureType type, double timeout)
+ {
+ EnsureHitBoxExists();
+ if (timeout > 0)
+ _gestureLayer.FlickTimeLimit = (int)(timeout * 1000);
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Move, (data) => { OnGestureMoved(type, data); });
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddRotateGesture(EGestureType type)
+ {
+ EnsureHitBoxExists();
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Move, (data) => { OnGestureMoved(type, data); });
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void AddMomentumGesture(EGestureType type)
+ {
+ EnsureHitBoxExists();
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Start, (data) => { OnGestureStarted(type, data); });
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Move, (data) => { OnGestureMoved(type, data); });
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.End, (data) => { OnGestureCompleted(type, data); });
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Abort, (data) => { OnGestureCanceled(type, data); });
+ }
+
+ void RemovePanGesture()
+ {
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, null);
+ }
+
+ void RemovePinchGesture()
+ {
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, null);
+ }
+
+ void RemoveTapGesture(EGestureType type)
+ {
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, null);
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.Abort, null);
+ }
+
+ void RemoveFlickGesture()
+ {
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetFlickCallback(GestureLayer.GestureState.Abort, null);
+ }
+
+ void RemoveRotateGesture()
+ {
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetRotateCallback(GestureLayer.GestureState.Abort, null);
+ }
+
+ void RemoveMomentumGesture()
+ {
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Abort, null);
+ }
+
+ #region GestureCallback
+
+ void OnGestureStarted(EGestureType type, object data)
+ {
+ var cache = _handlerCache;
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ (handler as IGestureController)?.SendStarted(View, data);
+ }
+ }
+ }
+
+ void OnGestureMoved(EGestureType type, object data)
+ {
+ var cache = _handlerCache;
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ (handler as IGestureController)?.SendMoved(View, data);
+ }
+ }
+ }
+
+ void OnGestureCompleted(EGestureType type, object data)
+ {
+ var cache = _handlerCache;
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ (handler as IGestureController)?.SendCompleted(View, data);
+ }
+ }
+ }
+
+ void OnGestureCanceled(EGestureType type, object data)
+ {
+ var cache = _handlerCache;
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ (handler as IGestureController)?.SendCanceled(View, data);
+ }
+ }
+ }
+
+ void OnDoubleTapStarted(EGestureType type, object data)
+ {
+ _doubleTapTime = ((GestureLayer.TapData)data).Timestamp;
+ OnGestureStarted(type, data);
+ }
+
+ void OnDoubleTapCompleted(EGestureType type, object data)
+ {
+ _doubleTapTime = ((GestureLayer.TapData)data).Timestamp - _doubleTapTime;
+ var cache = _handlerCache;
+
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ if ((handler.Timeout * 1000) >= _longTapTime)
+ (handler as IGestureController)?.SendCompleted(View, data);
+ else
+ (handler as IGestureController)?.SendCanceled(View, data);
+ }
+ }
+ }
+
+ void OnLongTapStarted(EGestureType type, object data)
+ {
+ _longTapTime = ((GestureLayer.TapData)data).Timestamp;
+ OnGestureStarted(type, data);
+ }
+
+ void OnLongTapCompleted(EGestureType type, object data)
+ {
+ _longTapTime = ((GestureLayer.TapData)data).Timestamp - _longTapTime;
+ var cache = _handlerCache;
+
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ if ((handler.Timeout * 1000) <= _longTapTime)
+ (handler as IGestureController)?.SendCompleted(View, data);
+ else
+ (handler as IGestureController)?.SendCanceled(View, data);
+ }
+ }
+ }
+
+ void OnFlickStarted(EGestureType type, object data)
+ {
+ var lineData = (GestureLayer.LineData)data;
+ _horizontalSwipeTime = Convert.ToInt32(lineData.HorizontalSwipeTimestamp);
+ _verticalSwipeTime = Convert.ToInt32(lineData.VerticalSwipeTimestamp);
+ OnGestureStarted(type, data);
+ }
+
+ void OnFlickCompleted(EGestureType type, object data)
+ {
+ var lineData = (GestureLayer.LineData)data;
+ _horizontalSwipeTime = Convert.ToInt32(lineData.HorizontalSwipeTimestamp - _horizontalSwipeTime);
+ _verticalSwipeTime = Convert.ToInt32(lineData.VerticalSwipeTimestamp - _verticalSwipeTime);
+ var cache = _handlerCache;
+
+ if (cache.ContainsKey(type))
+ {
+ foreach (var handler in cache[type])
+ {
+ if ((handler.Timeout * 1000) >= _horizontalSwipeTime ||
+ (handler.Timeout * 1000) >= _verticalSwipeTime)
+ (handler as IGestureController)?.SendCompleted(View, data);
+ else
+ (handler as IGestureController)?.SendCanceled(View, data);
+ }
+ }
+ }
+
+ #endregion GestureCallback
+
+ GestureHandler CreateHandler(IGestureRecognizer recognizer)
+ {
+ var handlerType = Registrar.Registered.GetHandlerType(recognizer.GetType());
+
+ if (handlerType != null)
+ return (GestureHandler)Activator.CreateInstance(handlerType, recognizer);
+ else
+ return null;
+ }
+
+ GestureHandler LookupHandler(IGestureRecognizer recognizer)
+ {
+ var cache = _handlerCache;
+
+ foreach (var handlers in cache.Values)
+ {
+ foreach (var handler in handlers)
+ {
+ if (handler.Recognizer == recognizer)
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ void UpdateTapGesture(GestureHandler handler)
+ {
+ RemoveGesture(handler.Recognizer);
+ AddGesture(handler.Recognizer);
+
+ if (handler.Timeout > _gestureLayer.DoubleTapTimeout)
+ _gestureLayer.DoubleTapTimeout = handler.Timeout;
+ }
+
+ void UpdateLongTapGesture(GestureHandler handler)
+ {
+ if (handler.Timeout > 0 && handler.Timeout < _gestureLayer.LongTapTimeout)
+ _gestureLayer.LongTapTimeout = handler.Timeout;
+ }
+
+ void UpdateFlickGesture(GestureHandler handler)
+ {
+ if (handler.Timeout > _gestureLayer.FlickTimeLimit)
+ _gestureLayer.FlickTimeLimit = (int)(handler.Timeout * 1000);
+ }
+
+ void OnGestureRecognizerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ var handler = sender as GestureHandler;
+ if (handler != null)
+ {
+ switch (handler.Type)
+ {
+ case EGestureType.Tap:
+ case EGestureType.DoubleTap:
+ case EGestureType.TripleTap:
+ UpdateTapGesture(handler);
+ break;
+
+ case EGestureType.LongTap:
+ UpdateLongTapGesture(handler);
+ break;
+
+ case EGestureType.Flick:
+ UpdateFlickGesture(handler);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ void OnGestureRecognizerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ // Gestures will be registered/unregistered according to changes in the GestureRecognizers list
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (_gestureLayer == null)
+ CreateGestureLayer();
+ AddGestures(e.NewItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ RemoveGestures(e.OldItems.OfType<IGestureRecognizer>());
+ AddGestures(e.NewItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ RemoveGestures(e.OldItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ RemoveGestures(View.GestureRecognizers);
+ break;
+ }
+ }
+ }
+} \ No newline at end of file