- Design : http://suprem.sec.samsung.net/confluence/display/SPTDTLC/Refactoring+GestureHandler
Change-Id: I1cbcf830b4b1076c227b0ea641508198c7cb2102
Signed-off-by: SungHyun Min <shyun.min@samsung.com>
--- /dev/null
+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
typeof(ExportRendererAttribute),
typeof(ExportImageSourceHandlerAttribute),
typeof(ExportCellAttribute),
+ typeof(ExportHandlerAttribute)
});
}
/// 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>
return base.VisitMember(node);
}
}
-}
+}
\ No newline at end of file
--- /dev/null
+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
-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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
[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
-using ElmSharp;
+using ElmSharp;
namespace Xamarin.Forms.Platform.Tizen
{
where TView : View
where TNativeView : Widget
{
+ GestureDetector _gestureDetector;
+
/// <summary>
/// Default constructor.
/// </summary>
if (e.OldElement != null)
{
- _gestureHandler.Clear();
- _gestureHandler = null;
+ _gestureDetector.Clear();
+ _gestureDetector = null;
}
if (e.NewElement != null)
{
- _gestureHandler = new GestureHandler(this);
+ _gestureDetector = new GestureDetector(this);
}
}
return (TNativeView)NativeView;
}
}
+
+ protected override void ApplyTransformation()
+ {
+ base.ApplyTransformation();
+ _gestureDetector?.UpdateHitBox();
+ }
}
-}
+}
\ No newline at end of file
HashSet<string> _batchedProperties = new HashSet<string>();
- internal GestureHandler _gestureHandler;
-
/// <summary>
/// Default constructor.
/// </summary>
}
}
-
protected virtual void UpdateLayout()
{
// we're updating the coordinates of native control only if they were modified
void OnMoved(object sender, EventArgs e)
{
ApplyTransformation();
- _gestureHandler?.UpdateHitBox();
}
void EnsureChildOrder()
NativeView.PassEvents = Element.InputTransparent;
}
- protected virtual void UpdateThemeStyle() {}
+ protected virtual void UpdateThemeStyle()
+ {
+ }
void ApplyRotation(EvasMap map, ERect geometry, ref bool changed)
{
}
}
- public void ApplyTransformation()
+ protected virtual void ApplyTransformation()
{
if (null == NativeView)
{
{
NativeView.EvasMap = map;
}
- _gestureHandler?.UpdateHitBox();
}
}
--s_ignoreCount;
}
}
-}
+}
\ No newline at end of file
--- /dev/null
+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
<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" />