summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Tizen/GestureHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.Tizen/GestureHandler.cs')
-rw-r--r--Xamarin.Forms.Platform.Tizen/GestureHandler.cs299
1 files changed, 299 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/GestureHandler.cs b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs
new file mode 100644
index 00000000..209da3e3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs
@@ -0,0 +1,299 @@
+using System.Linq;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Collections.Generic;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class GestureHandler
+ {
+ 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 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);
+ }
+
+ 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 AddAndRemoveRecognizers(IEnumerable<IGestureRecognizer> removed, IEnumerable<IGestureRecognizer> added)
+ {
+ 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);
+ }
+ }
+
+ 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;
+
+ case NotifyCollectionChangedAction.Replace:
+ AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), e.NewItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), null);
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ AddAndRemoveRecognizers(_view.GestureRecognizers, null);
+ break;
+ }
+ }
+
+ 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);
+ }
+
+ void OnPinchStarted(GestureLayer.ZoomData 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);
+ }
+
+ void OnPinchMoved(GestureLayer.ZoomData 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;
+ }
+
+ void OnPinchCompleted(GestureLayer.ZoomData data)
+ {
+ _currentPinchGestureController.SendPinchEnded(_view);
+ }
+
+ void OnPinchCancelled(GestureLayer.ZoomData data)
+ {
+ // ElmSharp says the gesture has been aborted really too often, report completion instead
+ _currentPinchGestureController.SendPinchEnded(_view);
+ }
+ }
+}