Refactoring GestureHandler for extension
authorSungHyun Min <shyun.min@samsung.com>
Wed, 22 Feb 2017 10:49:54 +0000 (19:49 +0900)
committerKangho Hur <kangho.hur@samsung.com>
Mon, 24 Apr 2017 04:36:55 +0000 (13:36 +0900)
 - Design : http://suprem.sec.samsung.net/confluence/display/SPTDTLC/Refactoring+GestureHandler

Change-Id: I1cbcf830b4b1076c227b0ea641508198c7cb2102
Signed-off-by: SungHyun Min <shyun.min@samsung.com>
12 files changed:
Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Forms.cs
Xamarin.Forms.Platform.Tizen/GestureDetector.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/GestureHandler.cs
Xamarin.Forms.Platform.Tizen/IGestureController.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/PanGestureHandler.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/PinchGestureHandler.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs
Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
Xamarin.Forms.Platform.Tizen/TapGestureHandler.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj [changed mode: 0644->0755]

diff --git a/Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs b/Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs
new file mode 100644 (file)
index 0000000..dc6a3ae
--- /dev/null
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+       public sealed class ExportHandlerAttribute : HandlerAttribute
+       {
+               public ExportHandlerAttribute(Type handler, Type target) : base(handler, target)
+               {
+               }
+       }
+}
\ No newline at end of file
index 1a2d7bd..177b8c5 100644 (file)
@@ -193,6 +193,7 @@ namespace Xamarin.Forms.Platform.Tizen
                                        typeof(ExportRendererAttribute),
                                        typeof(ExportImageSourceHandlerAttribute),
                                        typeof(ExportCellAttribute),
+                                       typeof(ExportHandlerAttribute)
                                });
                        }
 
@@ -247,7 +248,7 @@ namespace Xamarin.Forms.Platform.Tizen
                /// Converts the dp into pixel by using scaling factor.
                /// </summary>
                /// <remarks>
-               /// Use this API if you want to get pixel size from dp by using scaling factor. 
+               /// Use this API if you want to get pixel size from dp by using scaling factor.
                /// If the scaling factor is 1.0 by user setting, the same value is returned. This is mean that user doesn't want to pixel independent metric.
                /// </remarks>
                /// <param name="dp"></param>
@@ -330,4 +331,4 @@ namespace Xamarin.Forms.Platform.Tizen
                        return base.VisitMember(node);
                }
        }
-}
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/GestureDetector.cs b/Xamarin.Forms.Platform.Tizen/GestureDetector.cs
new file mode 100644 (file)
index 0000000..f2a3aac
--- /dev/null
@@ -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
index 209da3e..0d1bb93 100644 (file)
-using System.Linq;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Collections.Generic;
+using System;
+using System.ComponentModel;
 using ElmSharp;
-using EColor = ElmSharp.Color;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
-       internal class GestureHandler
+       public abstract class GestureHandler : IGestureController, INotifyPropertyChanged, IRegisterable
        {
-               internal readonly IVisualElementRenderer _renderer;
-               internal GestureLayer _gestureLayer;
-               View _view => _renderer.Element as View;
-               IPanGestureController _currentPanGestureController;
-               int _currentPanGestureId;
-               IPinchGestureController _currentPinchGestureController;
-               Point _currentScalePoint;
-               int _previousPinchRadius;
-               double _originalPinchScale;
-               Polygon _hitBox;
+               public IGestureRecognizer Recognizer { get; private set; }
 
-               public GestureHandler(IVisualElementRenderer renderer)
-               {
-                       _renderer = renderer;
-                       // Whenever a GestureRecognizer is added to the View, it will be connected to GestureLayer
-                       (_view.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged += OnGestureRecognizersChanged;
-                       // handle GestureRecognizers which were already set by the time we got here
-                       if (_view.GestureRecognizers.Count > 0)
-                       {
-                               CreateGestureLayer();
-                               foreach (var item in _view.GestureRecognizers)
-                                       ToggleRecognizer(item, true);
-                       }
-               }
-
-               public void Clear()
-               {
-                       // this will clear all callbacks in ElmSharp GestureLayer
-                       _gestureLayer.Unrealize();
-                       (_view.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged -= OnGestureRecognizersChanged;
-                       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);
-                       }
-               }
-
-               protected void ToggleRecognizer(IGestureRecognizer recognizer, bool enable)
-               {
-                       TapGestureRecognizer tapRecognizer;
-                       PanGestureRecognizer panRecognizer;
-                       PinchGestureRecognizer pinchRecognizer;
-
-                       if ((tapRecognizer = recognizer as TapGestureRecognizer) != null)
-                       {
-                               ToggleTapRecognizer(tapRecognizer, enable);
-                       }
-                       else if ((panRecognizer = recognizer as PanGestureRecognizer) != null)
-                       {
-                               if (enable)
-                                       AddPanRecognizer(panRecognizer);
-                               else
-                                       RemovePanRecognizer(panRecognizer);
-                       }
-                       else if ((pinchRecognizer = recognizer as PinchGestureRecognizer) != null)
-                       {
-                               if (enable)
-                                       AddPinchRecognizer(pinchRecognizer);
-                               else
-                                       RemovePinchRecognizer(pinchRecognizer);
-                       }
-                       else
-                       {
-                               Log.Error("Unknown GestureRecognizer will be ignored: {0}", recognizer);
-                       }
-               }
-
-               void ToggleTapRecognizer(TapGestureRecognizer recognizer, bool enable)
-               {
-                       GestureLayer.GestureType type;
-                       switch (recognizer.NumberOfTapsRequired)
-                       {
-                               case 1:
-                                       type = GestureLayer.GestureType.Tap;
-                                       break;
-                               case 2:
-                                       type = GestureLayer.GestureType.DoubleTap;
-                                       break;
-                               default:
-                                       type = GestureLayer.GestureType.TripleTap;
-                                       break;
-                       }
-                       if (enable)
-                               _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, (data) => recognizer.SendTapped(_view));
-                       else
-                               _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, null);
-               }
-
-               void AddPanRecognizer(PanGestureRecognizer recognizer)
-               {
-                       if (_currentPanGestureController != null)
-                               Log.Warn("More than one PanGestureRecognizer on {0}. Only the last one will work.", _view);
-                       EnsureHitBoxExists();
-                       _currentPanGestureController = recognizer;
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, OnPanStarted);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, OnPanMoved);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, OnPanCompleted);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, OnPanCancelled);
-               }
-
-               void RemovePanRecognizer(PanGestureRecognizer recognizer)
-               {
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, null);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, null);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, null);
-                       _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, null);
-                       _currentPanGestureController = null;
-               }
-
-               void AddPinchRecognizer(PinchGestureRecognizer recognizer)
-               {
-                       if (_currentPinchGestureController != null)
-                               Log.Warn("More than one PinchGestureRecognizer on {0}. Only the last one will work.", _view);
-                       EnsureHitBoxExists();
-                       _currentPinchGestureController = recognizer;
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, OnPinchStarted);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, OnPinchMoved);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, OnPinchCompleted);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, OnPinchCancelled);
-               }
-
-               void RemovePinchRecognizer(PinchGestureRecognizer recognizer)
-               {
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, null);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, null);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, null);
-                       _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, null);
-                       _currentPinchGestureController = null;
-               }
-
-               void CreateGestureLayer()
-               {
-                       _gestureLayer = new GestureLayer(_renderer.NativeView);
-                       _gestureLayer.Attach(_renderer.NativeView);
-               }
+               public abstract GestureLayer.GestureType Type { get; }
 
-               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);
-                       }
-               }
+               public virtual double Timeout { get; }
 
-               void AddAndRemoveRecognizers(IEnumerable<IGestureRecognizer> removed, IEnumerable<IGestureRecognizer> added)
+               protected GestureHandler(IGestureRecognizer recognizer)
                {
-                       if (_hitBox == null &&
-                               added != null &&
-                               added.Any(item => (item is IPanGestureController || item is IPinchGestureController)))
-                       {
-                               // at least one of the added recognizers requires _hitBot, which is not ready
-                               _gestureLayer.ClearCallbacks();
-                               EnsureHitBoxExists();
-                               // as _gestureLayer was reattached, register all callbacks, not only new ones
-                               removed = null;
-                               added = _view.GestureRecognizers;
-                       }
-
-                       if (removed != null)
-                       {
-                               foreach (var item in removed)
-                                       ToggleRecognizer(item, false);
-                       }
-                       if (added != null)
-                       {
-                               foreach (var item in added)
-                                       ToggleRecognizer(item, true);
-                       }
+                       Recognizer = recognizer;
                }
 
-               void OnGestureRecognizersChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
-               {
-                       // Gestures will be registered/unregistered according to changes in the GestureRecognizers list
-                       switch (e.Action)
-                       {
-                               case NotifyCollectionChangedAction.Add:
-                                       if (_gestureLayer == null)
-                                               CreateGestureLayer();
-                                       AddAndRemoveRecognizers(null, e.NewItems.OfType<IGestureRecognizer>());
-                                       break;
+               public virtual event PropertyChangedEventHandler PropertyChanged;
 
-                               case NotifyCollectionChangedAction.Replace:
-                                       AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), e.NewItems.OfType<IGestureRecognizer>());
-                                       break;
+               protected abstract void OnStarted(View sender, object data);
 
-                               case NotifyCollectionChangedAction.Remove:
-                                       AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), null);
-                                       break;
+               protected abstract void OnMoved(View sender, object data);
 
-                               case NotifyCollectionChangedAction.Reset:
-                                       AddAndRemoveRecognizers(_view.GestureRecognizers, null);
-                                       break;
-                       }
-               }
+               protected abstract void OnCompleted(View sender, object data);
 
-               void OnPanStarted(GestureLayer.LineData data)
-               {
-                       _currentPanGestureId++;
-                       _currentPanGestureController.SendPanStarted(_view, _currentPanGestureId);
-               }
-
-               void OnPanMoved(GestureLayer.LineData data)
-               {
-                       _currentPanGestureController.SendPan(_view, data.X2 - data.X1, data.Y2 - data.Y1, _currentPanGestureId);
-               }
-
-               void OnPanCompleted(GestureLayer.LineData data)
-               {
-                       _currentPanGestureController.SendPanCompleted(_view, _currentPanGestureId);
-               }
-
-               void OnPanCancelled(GestureLayer.LineData data)
-               {
-                       // don't trust ElmSharp that the gesture has been aborted, report that it is completed
-                       _currentPanGestureController.SendPanCompleted(_view, _currentPanGestureId);
-               }
+               protected abstract void OnCanceled(View sender, object data);
 
-               void OnPinchStarted(GestureLayer.ZoomData data)
+               void IGestureController.SendStarted(View sender, object data)
                {
-                       var geometry = _renderer.NativeView.Geometry;
-                       _currentScalePoint = new Point((data.X - geometry.X) / (double)geometry.Width, (data.Y - geometry.Y) / (double)geometry.Height);
-                       _originalPinchScale = _view.Scale;
-                       _previousPinchRadius = data.Radius;
-                       _currentPinchGestureController.SendPinchStarted(_view, _currentScalePoint);
+                       OnStarted(sender, data);
                }
 
-               void OnPinchMoved(GestureLayer.ZoomData data)
+               void IGestureController.SendCompleted(View sender, object data)
                {
-                       if (_previousPinchRadius <= 0)
-                               _previousPinchRadius = 1;
-                       // functionality limitation: _currentScalePoint is not updated
-                       _currentPinchGestureController.SendPinch(_view,
-                               1 + _originalPinchScale * (data.Radius - _previousPinchRadius) / _previousPinchRadius,
-                               _currentScalePoint
-                       );
-                       _previousPinchRadius = data.Radius;
+                       OnCompleted(sender, data);
                }
 
-               void OnPinchCompleted(GestureLayer.ZoomData data)
+               void IGestureController.SendMoved(View sender, object data)
                {
-                       _currentPinchGestureController.SendPinchEnded(_view);
+                       OnMoved(sender, data);
                }
 
-               void OnPinchCancelled(GestureLayer.ZoomData data)
+               void IGestureController.SendCanceled(View sender, object data)
                {
-                       // ElmSharp says the gesture has been aborted really too often, report completion instead
-                       _currentPinchGestureController.SendPinchEnded(_view);
+                       OnCanceled(sender, data);
                }
        }
-}
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/IGestureController.cs b/Xamarin.Forms.Platform.Tizen/IGestureController.cs
new file mode 100644 (file)
index 0000000..e29975f
--- /dev/null
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public interface IGestureController
+       {
+               void SendStarted(View sender, object data);
+
+               void SendMoved(View sender, object data);
+
+               void SendCompleted(View sender, object data);
+
+               void SendCanceled(View sender, object data);
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/PanGestureHandler.cs b/Xamarin.Forms.Platform.Tizen/PanGestureHandler.cs
new file mode 100644 (file)
index 0000000..ec1e6de
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class PanGestureHandler : GestureHandler
+       {
+               int _currentPanGestureId;
+
+               public PanGestureHandler(IGestureRecognizer recognizer) : base(recognizer)
+               {
+               }
+
+               public override GestureLayer.GestureType Type
+               {
+                       get
+                       {
+                               return GestureLayer.GestureType.Momentum;
+                       }
+               }
+
+               public override event PropertyChangedEventHandler PropertyChanged;
+
+               protected override void OnStarted(View sender, object data)
+               {
+                       _currentPanGestureId++;
+                       (Recognizer as IPanGestureController)?.SendPanStarted(sender, _currentPanGestureId);
+               }
+
+               protected override void OnMoved(View sender, object data)
+               {
+                       var lineData = (GestureLayer.MomentumData)data;
+                       (Recognizer as IPanGestureController)?.SendPan(sender, lineData.X2 - lineData.X1, lineData.Y2 - lineData.Y1, _currentPanGestureId);
+               }
+
+               protected override void OnCompleted(View sender, object data)
+               {
+                       (Recognizer as IPanGestureController)?.SendPanCompleted(sender, _currentPanGestureId);
+               }
+
+               protected override void OnCanceled(View sender, object data)
+               {
+                       (Recognizer as IPanGestureController)?.SendPanCanceled(sender, _currentPanGestureId);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/PinchGestureHandler.cs b/Xamarin.Forms.Platform.Tizen/PinchGestureHandler.cs
new file mode 100644 (file)
index 0000000..c22b7e2
--- /dev/null
@@ -0,0 +1,60 @@
+using System;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class PinchGestureHandler : GestureHandler
+       {
+               Point _currentScalePoint;
+               int _previousPinchRadius;
+               double _originalPinchScale;
+
+               public PinchGestureHandler(IGestureRecognizer recognizer) : base(recognizer)
+               {
+               }
+
+               public override GestureLayer.GestureType Type
+               {
+                       get
+                       {
+                               return GestureLayer.GestureType.Zoom;
+                       }
+               }
+
+               public override event PropertyChangedEventHandler PropertyChanged;
+
+               protected override void OnStarted(View sender, object data)
+               {
+                       var geometry = Platform.GetRenderer(sender).NativeView.Geometry;
+                       var zoomData = (GestureLayer.ZoomData)data;
+                       _currentScalePoint = new Point((zoomData.X - geometry.X) / (double)geometry.Width, (zoomData.Y - geometry.Y) / (double)geometry.Height);
+                       _originalPinchScale = sender.Scale;
+                       _previousPinchRadius = zoomData.Radius;
+                       (Recognizer as IPinchGestureController)?.SendPinchStarted(sender, _currentScalePoint);
+               }
+
+               protected override void OnMoved(View sender, object data)
+               {
+                       var zoomData = (GestureLayer.ZoomData)data;
+                       if (_previousPinchRadius <= 0)
+                               _previousPinchRadius = 1;
+                       // functionality limitation: _currentScalePoint is not updated
+                       (Recognizer as IPinchGestureController)?.SendPinch(sender,
+                               1 + _originalPinchScale * (zoomData.Radius - _previousPinchRadius) / _previousPinchRadius,
+                               _currentScalePoint
+                       );
+                       _previousPinchRadius = zoomData.Radius;
+               }
+
+               protected override void OnCompleted(View sender, object data)
+               {
+                       (Recognizer as IPinchGestureController)?.SendPinchEnded(sender);
+               }
+
+               protected override void OnCanceled(View sender, object data)
+               {
+                       (Recognizer as IPinchGestureController)?.SendPinchCanceled(sender);
+               }
+       }
+}
\ No newline at end of file
index 0b7977f..1b9c3e9 100644 (file)
@@ -59,3 +59,7 @@ using Xamarin.Forms.Platform.Tizen;
 [assembly: ExportCell(typeof(SwitchCell), typeof(SwitchCellRenderer))]
 [assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))]
 [assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))]
+
+[assembly: ExportHandler(typeof(TapGestureRecognizer), typeof(TapGestureHandler))]
+[assembly: ExportHandler(typeof(PinchGestureRecognizer), typeof(PinchGestureHandler))]
+[assembly: ExportHandler(typeof(PanGestureRecognizer), typeof(PanGestureHandler))]
\ No newline at end of file
index 24debc8..bf1891b 100644 (file)
@@ -1,4 +1,4 @@
-using ElmSharp;
+using ElmSharp;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
@@ -9,6 +9,8 @@ namespace Xamarin.Forms.Platform.Tizen
                where TView : View
                where TNativeView : Widget
        {
+               GestureDetector _gestureDetector;
+
                /// <summary>
                /// Default constructor.
                /// </summary>
@@ -22,13 +24,13 @@ namespace Xamarin.Forms.Platform.Tizen
 
                        if (e.OldElement != null)
                        {
-                               _gestureHandler.Clear();
-                               _gestureHandler = null;
+                               _gestureDetector.Clear();
+                               _gestureDetector = null;
                        }
 
                        if (e.NewElement != null)
                        {
-                               _gestureHandler = new GestureHandler(this);
+                               _gestureDetector = new GestureDetector(this);
                        }
                }
 
@@ -42,5 +44,11 @@ namespace Xamarin.Forms.Platform.Tizen
                                return (TNativeView)NativeView;
                        }
                }
+
+               protected override void ApplyTransformation()
+               {
+                       base.ApplyTransformation();
+                       _gestureDetector?.UpdateHitBox();
+               }
        }
-}
+}
\ No newline at end of file
index 394ec02..6baff19 100644 (file)
@@ -44,8 +44,6 @@ namespace Xamarin.Forms.Platform.Tizen
 
                HashSet<string> _batchedProperties = new HashSet<string>();
 
-               internal GestureHandler _gestureHandler;
-
                /// <summary>
                /// Default constructor.
                /// </summary>
@@ -241,7 +239,6 @@ namespace Xamarin.Forms.Platform.Tizen
                        }
                }
 
-
                protected virtual void UpdateLayout()
                {
                        // we're updating the coordinates of native control only if they were modified
@@ -727,7 +724,6 @@ namespace Xamarin.Forms.Platform.Tizen
                void OnMoved(object sender, EventArgs e)
                {
                        ApplyTransformation();
-                       _gestureHandler?.UpdateHitBox();
                }
 
                void EnsureChildOrder()
@@ -778,7 +774,9 @@ namespace Xamarin.Forms.Platform.Tizen
                        NativeView.PassEvents = Element.InputTransparent;
                }
 
-               protected virtual void UpdateThemeStyle() {}
+               protected virtual void UpdateThemeStyle()
+               {
+               }
 
                void ApplyRotation(EvasMap map, ERect geometry, ref bool changed)
                {
@@ -843,7 +841,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        }
                }
 
-               public void ApplyTransformation()
+               protected virtual void ApplyTransformation()
                {
                        if (null == NativeView)
                        {
@@ -866,7 +864,6 @@ namespace Xamarin.Forms.Platform.Tizen
                        {
                                NativeView.EvasMap = map;
                        }
-                       _gestureHandler?.UpdateHitBox();
                }
        }
 
@@ -893,4 +890,4 @@ namespace Xamarin.Forms.Platform.Tizen
                        --s_ignoreCount;
                }
        }
-}
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/TapGestureHandler.cs b/Xamarin.Forms.Platform.Tizen/TapGestureHandler.cs
new file mode 100644 (file)
index 0000000..f8e0907
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class TapGestureHandler : GestureHandler
+       {
+               public TapGestureHandler(IGestureRecognizer recognizer) : base(recognizer)
+               {
+                       recognizer.PropertyChanged += OnRecognizerPropertyChanged;
+               }
+
+               public override GestureLayer.GestureType Type
+               {
+                       get
+                       {
+                               var recognizer = Recognizer as TapGestureRecognizer;
+                               if (recognizer != null)
+                               {
+                                       int numberOfTaps = recognizer.NumberOfTapsRequired;
+
+                                       if (numberOfTaps > 2)
+                                               return GestureLayer.GestureType.TripleTap;
+                                       else if (numberOfTaps > 1)
+                                               return GestureLayer.GestureType.DoubleTap;
+                               }
+                               return GestureLayer.GestureType.Tap;
+                       }
+               }
+
+               public override event PropertyChangedEventHandler PropertyChanged;
+
+               protected override void OnStarted(View sender, object data)
+               {
+               }
+
+               protected override void OnMoved(View sender, object data)
+               {
+               }
+
+               protected override void OnCompleted(View sender, object data)
+               {
+                       (Recognizer as TapGestureRecognizer)?.SendTapped(sender);
+               }
+
+               protected override void OnCanceled(View sender, object data)
+               {
+               }
+
+               void OnRecognizerPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       PropertyChanged?.Invoke(this, e);
+               }
+       }
+}
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index c51c4e7..1d50c33
@@ -48,6 +48,7 @@
     <Compile Include="ExportImageSourceHandlerAttribute.cs" />
     <Compile Include="ExportCellAttribute.cs" />
     <Compile Include="ExportRendererAttribute.cs" />
+    <Compile Include="ExportRendererHandler.cs" />
     <Compile Include="Extensions\PlatformConfigurationExtensions.cs" />
     <Compile Include="Extensions\ColorExtensions.cs" />
     <Compile Include="Extensions\KeyboardExtensions.cs" />
     <Compile Include="Extensions\ScrollToPositionExtensions.cs" />
     <Compile Include="Extensions\TextAlignmentExtensions.cs" />
     <Compile Include="Forms.cs" />
+    <Compile Include="GestureDetector.cs" />
     <Compile Include="GestureHandler.cs" />
+    <Compile Include="IGestureController.cs" />
+    <Compile Include="TapGestureHandler.cs" />
+    <Compile Include="PinchGestureHandler.cs" />
+    <Compile Include="PanGestureHandler.cs" />
     <Compile Include="FormsApplication.cs" />
     <Compile Include="Log\ConsoleLogger.cs" />
     <Compile Include="Log\DlogLogger.cs" />