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