2020-05-19 03:56:25 +03:00
|
|
|
using System;
|
2016-03-22 23:02:25 +03:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
using System.ComponentModel;
|
|
|
|
using System.Linq;
|
2020-05-19 03:56:25 +03:00
|
|
|
using global::Windows.UI.Xaml;
|
|
|
|
using global::Windows.UI.Xaml.Input;
|
|
|
|
using global::Windows.UI.Xaml.Media;
|
|
|
|
using System.Maui.Internals;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2020-05-19 03:56:25 +03:00
|
|
|
namespace System.Maui.Platform.UWP
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
public class VisualElementTracker<TElement, TNativeElement> : IDisposable where TElement : VisualElement where TNativeElement : FrameworkElement
|
|
|
|
{
|
|
|
|
readonly NotifyCollectionChangedEventHandler _collectionChangedHandler;
|
|
|
|
readonly List<uint> _fingers = new List<uint>();
|
|
|
|
FrameworkElement _container;
|
|
|
|
TNativeElement _control;
|
|
|
|
TElement _element;
|
|
|
|
|
|
|
|
bool _invalidateArrangeNeeded;
|
|
|
|
|
|
|
|
bool _isDisposed;
|
|
|
|
bool _isPanning;
|
2018-05-28 13:49:05 +03:00
|
|
|
bool _isSwiping;
|
2016-03-22 23:02:25 +03:00
|
|
|
bool _isPinching;
|
|
|
|
bool _wasPanGestureStartedSent;
|
|
|
|
bool _wasPinchGestureStartedSent;
|
|
|
|
|
|
|
|
public VisualElementTracker()
|
|
|
|
{
|
|
|
|
_collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged;
|
|
|
|
}
|
|
|
|
|
|
|
|
public FrameworkElement Container
|
|
|
|
{
|
|
|
|
get { return _container; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (_container == value)
|
|
|
|
return;
|
|
|
|
|
2017-12-05 21:49:58 +03:00
|
|
|
ClearContainerEventHandlers();
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
_container = value;
|
|
|
|
|
|
|
|
UpdatingGestureRecognizers();
|
|
|
|
|
|
|
|
UpdateNativeControl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 11:52:45 +03:00
|
|
|
public bool PreventGestureBubbling { get; set; }
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
public TNativeElement Control
|
|
|
|
{
|
|
|
|
get { return _control; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (_control == value)
|
|
|
|
return;
|
|
|
|
|
2017-03-29 11:52:45 +03:00
|
|
|
if (_control != null)
|
|
|
|
{
|
|
|
|
_control.Tapped -= HandleTapped;
|
|
|
|
_control.DoubleTapped -= HandleDoubleTapped;
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
_control = value;
|
|
|
|
UpdateNativeControl();
|
2017-03-29 11:52:45 +03:00
|
|
|
|
|
|
|
if (PreventGestureBubbling)
|
|
|
|
{
|
|
|
|
UpdatingGestureRecognizers();
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public TElement Element
|
|
|
|
{
|
|
|
|
get { return _element; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (_element == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (_element != null)
|
|
|
|
{
|
|
|
|
_element.BatchCommitted -= OnRedrawNeeded;
|
|
|
|
_element.PropertyChanged -= OnPropertyChanged;
|
|
|
|
|
|
|
|
var view = _element as View;
|
|
|
|
if (view != null)
|
|
|
|
{
|
|
|
|
var oldRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
|
|
|
|
oldRecognizers.CollectionChanged -= _collectionChangedHandler;
|
2018-08-31 14:32:37 +03:00
|
|
|
((view as IGestureController)?.CompositeGestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged -= _collectionChangedHandler;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_element = value;
|
|
|
|
|
|
|
|
if (_element != null)
|
|
|
|
{
|
|
|
|
_element.BatchCommitted += OnRedrawNeeded;
|
|
|
|
_element.PropertyChanged += OnPropertyChanged;
|
|
|
|
|
|
|
|
var view = _element as View;
|
|
|
|
if (view != null)
|
|
|
|
{
|
|
|
|
var newRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
|
|
|
|
newRecognizers.CollectionChanged += _collectionChangedHandler;
|
2018-08-31 14:32:37 +03:00
|
|
|
((view as IGestureController)?.CompositeGestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged += _collectionChangedHandler;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateNativeControl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
Dispose(true);
|
2017-12-05 21:49:58 +03:00
|
|
|
GC.SuppressFinalize(this);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public event EventHandler Updated;
|
|
|
|
|
2017-12-05 21:49:58 +03:00
|
|
|
void ClearContainerEventHandlers()
|
|
|
|
{
|
|
|
|
if (_container != null)
|
|
|
|
{
|
|
|
|
_container.Tapped -= OnTap;
|
|
|
|
_container.DoubleTapped -= OnDoubleTap;
|
|
|
|
_container.ManipulationDelta -= OnManipulationDelta;
|
|
|
|
_container.ManipulationStarted -= OnManipulationStarted;
|
|
|
|
_container.ManipulationCompleted -= OnManipulationCompleted;
|
|
|
|
_container.PointerPressed -= OnPointerPressed;
|
|
|
|
_container.PointerExited -= OnPointerExited;
|
|
|
|
_container.PointerReleased -= OnPointerReleased;
|
|
|
|
_container.PointerCanceled -= OnPointerCanceled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
{
|
|
|
|
if (_isDisposed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_isDisposed = true;
|
|
|
|
|
|
|
|
if (!disposing)
|
|
|
|
return;
|
|
|
|
|
2017-12-05 21:49:58 +03:00
|
|
|
ClearContainerEventHandlers();
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
if (_element != null)
|
|
|
|
{
|
|
|
|
_element.BatchCommitted -= OnRedrawNeeded;
|
|
|
|
_element.PropertyChanged -= OnPropertyChanged;
|
|
|
|
|
|
|
|
var view = _element as View;
|
|
|
|
if (view != null)
|
|
|
|
{
|
|
|
|
var oldRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
|
|
|
|
oldRecognizers.CollectionChanged -= _collectionChangedHandler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 11:52:45 +03:00
|
|
|
if (_control != null)
|
|
|
|
{
|
|
|
|
_control.Tapped -= HandleTapped;
|
|
|
|
_control.DoubleTapped -= HandleDoubleTapped;
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
Control = null;
|
|
|
|
Element = null;
|
|
|
|
Container = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
{
|
|
|
|
if (Element.Batched)
|
|
|
|
{
|
|
|
|
if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName ||
|
|
|
|
e.PropertyName == VisualElement.HeightProperty.PropertyName)
|
|
|
|
{
|
|
|
|
_invalidateArrangeNeeded = true;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName ||
|
|
|
|
e.PropertyName == VisualElement.HeightProperty.PropertyName)
|
|
|
|
{
|
|
|
|
MaybeInvalidate();
|
|
|
|
}
|
|
|
|
else if (e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateScaleAndRotation(Element, Container);
|
|
|
|
}
|
2018-05-07 15:22:56 +03:00
|
|
|
else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName || e.PropertyName == VisualElement.ScaleXProperty.PropertyName || e.PropertyName == VisualElement.ScaleYProperty.PropertyName)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
UpdateScaleAndRotation(Element, Container);
|
|
|
|
}
|
|
|
|
else if (e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName ||
|
|
|
|
e.PropertyName == VisualElement.RotationProperty.PropertyName || e.PropertyName == VisualElement.RotationXProperty.PropertyName || e.PropertyName == VisualElement.RotationYProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateRotation(Element, Container);
|
|
|
|
}
|
|
|
|
else if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateVisibility(Element, Container);
|
|
|
|
}
|
|
|
|
else if (e.PropertyName == VisualElement.OpacityProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateOpacity(Element, Container);
|
|
|
|
}
|
|
|
|
else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateInputTransparent(Element, Container);
|
|
|
|
}
|
2017-06-26 21:13:37 +03:00
|
|
|
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
|
|
|
|
{
|
|
|
|
UpdateInputTransparent(Element, Container);
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void UpdateNativeControl()
|
|
|
|
{
|
|
|
|
if (Element == null || Container == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
UpdateVisibility(Element, Container);
|
|
|
|
UpdateOpacity(Element, Container);
|
|
|
|
UpdateScaleAndRotation(Element, Container);
|
|
|
|
UpdateInputTransparent(Element, Container);
|
|
|
|
|
|
|
|
if (_invalidateArrangeNeeded)
|
|
|
|
{
|
|
|
|
MaybeInvalidate();
|
|
|
|
}
|
|
|
|
_invalidateArrangeNeeded = false;
|
|
|
|
|
|
|
|
OnUpdated();
|
|
|
|
}
|
|
|
|
|
2018-05-28 13:49:05 +03:00
|
|
|
void HandleSwipe(ManipulationDeltaRoutedEventArgs e, View view)
|
|
|
|
{
|
|
|
|
if (_fingers.Count > 1 || view == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_isSwiping = true;
|
|
|
|
|
|
|
|
foreach (SwipeGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<SwipeGestureRecognizer>())
|
|
|
|
{
|
|
|
|
((ISwipeGestureController)recognizer).SendSwipe(view, e.Delta.Translation.X + e.Cumulative.Translation.X, e.Delta.Translation.Y + e.Cumulative.Translation.Y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
void HandlePan(ManipulationDeltaRoutedEventArgs e, View view)
|
|
|
|
{
|
|
|
|
if (view == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_isPanning = true;
|
|
|
|
|
|
|
|
foreach (PanGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _fingers.Count))
|
|
|
|
{
|
|
|
|
if (!_wasPanGestureStartedSent)
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPanStarted(view, Application.Current.PanGestureId);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPan(view, e.Delta.Translation.X + e.Cumulative.Translation.X, e.Delta.Translation.Y + e.Cumulative.Translation.Y, Application.Current.PanGestureId);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
_wasPanGestureStartedSent = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandlePinch(ManipulationDeltaRoutedEventArgs e, View view)
|
|
|
|
{
|
|
|
|
if (_fingers.Count < 2 || view == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_isPinching = true;
|
|
|
|
|
2020-05-19 03:56:25 +03:00
|
|
|
global::Windows.Foundation.Point translationPoint = e.Container.TransformToVisual(Container).TransformPoint(e.Position);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
var scaleOriginPoint = new Point(translationPoint.X / view.Width, translationPoint.Y / view.Height);
|
|
|
|
IEnumerable<PinchGestureRecognizer> pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
|
|
|
|
foreach (PinchGestureRecognizer recognizer in pinchGestures)
|
|
|
|
{
|
|
|
|
if (!_wasPinchGestureStartedSent)
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPinchStarted(view, scaleOriginPoint);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPinch(view, e.Delta.Scale, scaleOriginPoint);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
_wasPinchGestureStartedSent = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaybeInvalidate()
|
|
|
|
{
|
|
|
|
if (Element.IsInNativeLayout)
|
|
|
|
return;
|
2017-03-09 21:39:27 +03:00
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
var parent = (FrameworkElement)Container.Parent;
|
|
|
|
parent?.InvalidateMeasure();
|
|
|
|
Container.InvalidateMeasure();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelGestureRecognizersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
|
|
|
|
{
|
|
|
|
UpdatingGestureRecognizers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnDoubleTap(object sender, DoubleTappedRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null)
|
|
|
|
return;
|
|
|
|
|
2018-05-07 21:02:28 +03:00
|
|
|
var tapPosition = e.GetPosition(Control);
|
|
|
|
var children = (view as IGestureController)?.GetChildElements(new Point(tapPosition.X, tapPosition.Y));
|
|
|
|
|
|
|
|
if (children != null)
|
|
|
|
foreach (var recognizer in children.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2))
|
|
|
|
{
|
|
|
|
recognizer.SendTapped(view);
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.Handled)
|
|
|
|
return;
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
IEnumerable<TapGestureRecognizer> doubleTapGestures = view.GestureRecognizers.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2);
|
|
|
|
foreach (TapGestureRecognizer recognizer in doubleTapGestures)
|
2018-05-07 21:02:28 +03:00
|
|
|
{
|
2016-03-22 23:02:25 +03:00
|
|
|
recognizer.SendTapped(view);
|
2018-05-07 21:02:28 +03:00
|
|
|
e.Handled = true;
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
|
|
|
|
{
|
2018-05-28 13:49:05 +03:00
|
|
|
SwipeComplete(true);
|
2016-03-22 23:02:25 +03:00
|
|
|
PinchComplete(true);
|
|
|
|
PanComplete(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null)
|
|
|
|
return;
|
2018-05-28 13:49:05 +03:00
|
|
|
HandleSwipe(e, view);
|
2016-03-22 23:02:25 +03:00
|
|
|
HandlePinch(e, view);
|
|
|
|
HandlePan(e, view);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null)
|
|
|
|
return;
|
|
|
|
_wasPinchGestureStartedSent = false;
|
|
|
|
_wasPanGestureStartedSent = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
uint id = e.Pointer.PointerId;
|
|
|
|
if (_fingers.Contains(id))
|
|
|
|
_fingers.Remove(id);
|
2018-05-28 13:49:05 +03:00
|
|
|
SwipeComplete(false);
|
2016-03-22 23:02:25 +03:00
|
|
|
PinchComplete(false);
|
|
|
|
PanComplete(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnPointerExited(object sender, PointerRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
uint id = e.Pointer.PointerId;
|
|
|
|
if (_fingers.Contains(id))
|
|
|
|
_fingers.Remove(id);
|
2018-05-28 13:49:05 +03:00
|
|
|
SwipeComplete(true);
|
2016-03-22 23:02:25 +03:00
|
|
|
PinchComplete(true);
|
|
|
|
PanComplete(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
uint id = e.Pointer.PointerId;
|
|
|
|
if (!_fingers.Contains(id))
|
|
|
|
_fingers.Add(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
uint id = e.Pointer.PointerId;
|
|
|
|
if (_fingers.Contains(id))
|
|
|
|
_fingers.Remove(id);
|
2018-05-28 13:49:05 +03:00
|
|
|
SwipeComplete(true);
|
2016-03-22 23:02:25 +03:00
|
|
|
PinchComplete(true);
|
|
|
|
PanComplete(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnRedrawNeeded(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
UpdateNativeControl();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnTap(object sender, TappedRoutedEventArgs e)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null)
|
|
|
|
return;
|
|
|
|
|
2018-05-07 21:02:28 +03:00
|
|
|
var tapPosition = e.GetPosition(Control);
|
|
|
|
var children = (view as IGestureController)?.GetChildElements(new Point(tapPosition.X, tapPosition.Y));
|
|
|
|
|
|
|
|
if (children != null)
|
|
|
|
foreach (var recognizer in children.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1))
|
|
|
|
{
|
|
|
|
recognizer.SendTapped(view);
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.Handled)
|
|
|
|
return;
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
IEnumerable<TapGestureRecognizer> tapGestures = view.GestureRecognizers.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1);
|
2018-05-07 21:02:28 +03:00
|
|
|
foreach (var recognizer in tapGestures)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
recognizer.SendTapped(view);
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-28 13:49:05 +03:00
|
|
|
void SwipeComplete(bool success)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null || !_isSwiping)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
foreach (SwipeGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<SwipeGestureRecognizer>())
|
|
|
|
{
|
|
|
|
((ISwipeGestureController)recognizer).DetectSwipe(view, recognizer.Direction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_isSwiping = false;
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
void OnUpdated()
|
|
|
|
{
|
|
|
|
if (Updated != null)
|
|
|
|
Updated(this, EventArgs.Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PanComplete(bool success)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null || !_isPanning)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (PanGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _fingers.Count))
|
|
|
|
{
|
|
|
|
if (success)
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPanCompleted(view, Application.Current.PanGestureId);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPanCanceled(view, Application.Current.PanGestureId);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Application.Current.PanGestureId++;
|
|
|
|
_isPanning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PinchComplete(bool success)
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
if (view == null || !_isPinching)
|
|
|
|
return;
|
|
|
|
|
|
|
|
IEnumerable<PinchGestureRecognizer> pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
|
|
|
|
foreach (PinchGestureRecognizer recognizer in pinchGestures)
|
|
|
|
{
|
|
|
|
if (success)
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPinchEnded(view);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
recognizer.SendPinchCanceled(view);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_isPinching = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateInputTransparent(VisualElement view, FrameworkElement frameworkElement)
|
|
|
|
{
|
2018-02-01 13:47:24 +03:00
|
|
|
if (view is Layout)
|
|
|
|
{
|
|
|
|
// Let VisualElementRenderer handle this
|
|
|
|
}
|
|
|
|
|
2017-06-20 18:40:07 +03:00
|
|
|
frameworkElement.IsHitTestVisible = view.IsEnabled && !view.InputTransparent;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateOpacity(VisualElement view, FrameworkElement frameworkElement)
|
|
|
|
{
|
|
|
|
frameworkElement.Opacity = view.Opacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateRotation(VisualElement view, FrameworkElement frameworkElement)
|
|
|
|
{
|
|
|
|
double anchorX = view.AnchorX;
|
|
|
|
double anchorY = view.AnchorY;
|
|
|
|
double rotationX = view.RotationX;
|
|
|
|
double rotationY = view.RotationY;
|
|
|
|
double rotation = view.Rotation;
|
|
|
|
double translationX = view.TranslationX;
|
|
|
|
double translationY = view.TranslationY;
|
2018-08-03 08:37:58 +03:00
|
|
|
double scaleX = view.Scale * view.ScaleX;
|
|
|
|
double scaleY = view.Scale * view.ScaleY;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2018-08-03 08:37:58 +03:00
|
|
|
if (rotationX % 360 == 0 && rotationY % 360 == 0 && rotation % 360 == 0 && translationX == 0 && translationY == 0 && scaleX == 1 && scaleY == 1)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
frameworkElement.Projection = null;
|
2019-11-11 21:50:53 +03:00
|
|
|
frameworkElement.RenderTransform = null;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-09 17:57:05 +03:00
|
|
|
// PlaneProjection removes touch and scrollwheel functionality on scrollable views such
|
|
|
|
// as ScrollView, ListView, and TableView. If neither RotationX or RotationY are set
|
|
|
|
// (i.e. their absolute value is 0), a CompositeTransform is instead used to allow for
|
|
|
|
// rotation of the control on a 2D plane, and the other values are set. Otherwise, the
|
|
|
|
// rotation values are set, but the aforementioned functionality will be lost.
|
2018-06-09 00:18:48 +03:00
|
|
|
if (Math.Abs(view.RotationX) != 0 || Math.Abs(view.RotationY) != 0)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-11-09 17:57:05 +03:00
|
|
|
frameworkElement.Projection = new PlaneProjection
|
|
|
|
{
|
|
|
|
CenterOfRotationX = anchorX,
|
|
|
|
CenterOfRotationY = anchorY,
|
2018-08-03 08:37:58 +03:00
|
|
|
GlobalOffsetX = scaleX == 0 ? 0 : translationX / scaleX,
|
|
|
|
GlobalOffsetY = scaleY == 0 ? 0 : translationY / scaleY,
|
2017-11-09 17:57:05 +03:00
|
|
|
RotationX = -rotationX,
|
|
|
|
RotationY = -rotationY,
|
|
|
|
RotationZ = -rotation
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
frameworkElement.RenderTransform = new CompositeTransform
|
|
|
|
{
|
|
|
|
CenterX = anchorX,
|
|
|
|
CenterY = anchorY,
|
|
|
|
Rotation = rotation,
|
2018-08-03 08:37:58 +03:00
|
|
|
ScaleX = scaleX,
|
|
|
|
ScaleY = scaleY,
|
|
|
|
TranslateX = scaleX == 0 ? 0 : translationX / scaleX,
|
|
|
|
TranslateY = scaleY == 0 ? 0 : translationY / scaleY
|
2017-11-09 17:57:05 +03:00
|
|
|
};
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateScaleAndRotation(VisualElement view, FrameworkElement frameworkElement)
|
|
|
|
{
|
|
|
|
double anchorX = view.AnchorX;
|
|
|
|
double anchorY = view.AnchorY;
|
2020-05-19 03:56:25 +03:00
|
|
|
frameworkElement.RenderTransformOrigin = new global::Windows.Foundation.Point(anchorX, anchorY);
|
2018-05-07 15:22:56 +03:00
|
|
|
frameworkElement.RenderTransform = new ScaleTransform { ScaleX = view.Scale * view.ScaleX, ScaleY = view.Scale * view.ScaleY };
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
UpdateRotation(view, frameworkElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateVisibility(VisualElement view, FrameworkElement frameworkElement)
|
|
|
|
{
|
|
|
|
frameworkElement.Visibility = view.IsVisible ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UpdatingGestureRecognizers()
|
|
|
|
{
|
|
|
|
var view = Element as View;
|
|
|
|
IList<IGestureRecognizer> gestures = view?.GestureRecognizers;
|
|
|
|
|
|
|
|
if (_container == null || gestures == null)
|
|
|
|
return;
|
|
|
|
|
2017-12-05 21:49:58 +03:00
|
|
|
ClearContainerEventHandlers();
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2018-05-07 21:02:28 +03:00
|
|
|
var children = (view as IGestureController)?.GetChildElements(Point.Zero);
|
|
|
|
IList<TapGestureRecognizer> childGestures = children?.GetChildGesturesFor<TapGestureRecognizer>().ToList();
|
2018-08-31 14:32:37 +03:00
|
|
|
|
2018-05-07 21:02:28 +03:00
|
|
|
if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any()
|
|
|
|
|| children?.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any() == true)
|
2017-03-29 11:52:45 +03:00
|
|
|
{
|
2016-03-22 23:02:25 +03:00
|
|
|
_container.Tapped += OnTap;
|
2017-03-29 11:52:45 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_control != null && PreventGestureBubbling)
|
|
|
|
{
|
|
|
|
_control.Tapped += HandleTapped;
|
|
|
|
}
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2018-05-07 21:02:28 +03:00
|
|
|
if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2).Any()
|
|
|
|
|| children?.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2).Any() == true)
|
2018-08-31 14:32:37 +03:00
|
|
|
{
|
2016-03-22 23:02:25 +03:00
|
|
|
_container.DoubleTapped += OnDoubleTap;
|
2017-03-29 11:52:45 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_control != null && PreventGestureBubbling)
|
|
|
|
{
|
|
|
|
_control.DoubleTapped += HandleDoubleTapped;
|
|
|
|
}
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2018-05-28 13:49:05 +03:00
|
|
|
bool hasSwipeGesture = gestures.GetGesturesFor<SwipeGestureRecognizer>().GetEnumerator().MoveNext();
|
2016-03-22 23:02:25 +03:00
|
|
|
bool hasPinchGesture = gestures.GetGesturesFor<PinchGestureRecognizer>().GetEnumerator().MoveNext();
|
|
|
|
bool hasPanGesture = gestures.GetGesturesFor<PanGestureRecognizer>().GetEnumerator().MoveNext();
|
2018-06-27 13:30:27 +03:00
|
|
|
if (!hasSwipeGesture && !hasPinchGesture && !hasPanGesture)
|
2016-03-22 23:02:25 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
//We can't handle ManipulationMode.Scale and System , so we don't support pinch/pan on a scrollview
|
|
|
|
if (Element is ScrollView)
|
|
|
|
{
|
|
|
|
if (hasPinchGesture)
|
|
|
|
Log.Warning("Gestures", "PinchGestureRecognizer is not supported on a ScrollView in Windows Platforms");
|
|
|
|
if (hasPanGesture)
|
|
|
|
Log.Warning("Gestures", "PanGestureRecognizer is not supported on a ScrollView in Windows Platforms");
|
2018-05-28 13:49:05 +03:00
|
|
|
if (hasSwipeGesture)
|
|
|
|
Log.Warning("Gestures", "SwipeGestureRecognizer is not supported on a ScrollView in Windows Platforms");
|
2016-03-22 23:02:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_container.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
|
|
|
|
_container.ManipulationDelta += OnManipulationDelta;
|
|
|
|
_container.ManipulationStarted += OnManipulationStarted;
|
|
|
|
_container.ManipulationCompleted += OnManipulationCompleted;
|
|
|
|
_container.PointerPressed += OnPointerPressed;
|
|
|
|
_container.PointerExited += OnPointerExited;
|
|
|
|
_container.PointerReleased += OnPointerReleased;
|
|
|
|
_container.PointerCanceled += OnPointerCanceled;
|
|
|
|
}
|
2017-03-29 11:52:45 +03:00
|
|
|
|
|
|
|
void HandleTapped(object sender, TappedRoutedEventArgs tappedRoutedEventArgs)
|
|
|
|
{
|
|
|
|
tappedRoutedEventArgs.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleDoubleTapped(object sender, DoubleTappedRoutedEventArgs doubleTappedRoutedEventArgs)
|
|
|
|
{
|
|
|
|
doubleTappedRoutedEventArgs.Handled = true;
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
2018-05-07 15:22:56 +03:00
|
|
|
}
|