diff options
author | SungHyun Min <shyun.min@samsung.com> | 2017-02-22 19:49:54 +0900 |
---|---|---|
committer | Kangho Hur <kangho.hur@samsung.com> | 2017-07-10 11:11:19 +0900 |
commit | 7e7d6ff4f19bcde57baafdb48ac2c3060ec692ee (patch) | |
tree | d8ffd5c12876fe2a5b6d327fc5f9a70948b14d8b | |
parent | 0cab4fe6d376deaeb3df9ae3303e0f472b458cbd (diff) | |
download | xamarin-forms-7e7d6ff4f19bcde57baafdb48ac2c3060ec692ee.tar.gz xamarin-forms-7e7d6ff4f19bcde57baafdb48ac2c3060ec692ee.tar.bz2 xamarin-forms-7e7d6ff4f19bcde57baafdb48ac2c3060ec692ee.zip |
Refactoring GestureHandler for extension
- Design : http://suprem.sec.samsung.net/confluence/display/SPTDTLC/Refactoring+GestureHandler
Change-Id: I1cbcf830b4b1076c227b0ea641508198c7cb2102
Signed-off-by: SungHyun Min <shyun.min@samsung.com>
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs | 12 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/Forms.cs | 5 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/GestureDetector.cs | 581 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/GestureHandler.cs | 293 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/IGestureController.cs | 15 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/PanGestureHandler.cs | 47 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/PinchGestureHandler.cs | 60 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs | 4 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs | 18 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs | 13 | ||||
-rw-r--r-- | Xamarin.Forms.Platform.Tizen/TapGestureHandler.cs | 56 | ||||
-rwxr-xr-x[-rw-r--r--] | Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj | 6 |
12 files changed, 824 insertions, 286 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs b/Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs new file mode 100644 index 00000000..dc6a3ae6 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/ExportRendererHandler.cs @@ -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 diff --git a/Xamarin.Forms.Platform.Tizen/Forms.cs b/Xamarin.Forms.Platform.Tizen/Forms.cs index 1a2d7bd6..177b8c55 100644 --- a/Xamarin.Forms.Platform.Tizen/Forms.cs +++ b/Xamarin.Forms.Platform.Tizen/Forms.cs @@ -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 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 diff --git a/Xamarin.Forms.Platform.Tizen/GestureHandler.cs b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs index 209da3e3..0d1bb93d 100644 --- a/Xamarin.Forms.Platform.Tizen/GestureHandler.cs +++ b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs @@ -1,299 +1,50 @@ -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 index 00000000..e29975fd --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/IGestureController.cs @@ -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 index 00000000..ec1e6de6 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/PanGestureHandler.cs @@ -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 index 00000000..c22b7e20 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/PinchGestureHandler.cs @@ -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 diff --git a/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs index 0b7977f6..1b9c3e9a 100644 --- a/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs @@ -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 diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs index 24debc8b..bf1891bf 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs @@ -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 diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs index 394ec02b..6baff194 100644 --- a/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs @@ -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 index 00000000..f8e09078 --- /dev/null +++ b/Xamarin.Forms.Platform.Tizen/TapGestureHandler.cs @@ -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 diff --git a/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj index c51c4e78..1d50c336 100644..100755 --- a/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj +++ b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj @@ -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" /> @@ -57,7 +58,12 @@ <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" /> |