From 88a4798acbfb5e9cdd93b6cf1124400a84d475c2 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Sat, 9 Dec 2017 20:39:10 -0800 Subject: [PATCH] Track tap gestures --- Ooui.Forms/EventTracker.cs | 169 ++++++++++++++++++++++++++++ Ooui.Forms/Platform.cs | 28 ++++- Ooui.Forms/VisualElementRenderer.cs | 9 +- 3 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 Ooui.Forms/EventTracker.cs diff --git a/Ooui.Forms/EventTracker.cs b/Ooui.Forms/EventTracker.cs new file mode 100644 index 0000000..d9eb5b5 --- /dev/null +++ b/Ooui.Forms/EventTracker.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using Xamarin.Forms; + +using NativeView = Ooui.Element; + +namespace Ooui.Forms +{ + public class EventTracker + { + readonly NotifyCollectionChangedEventHandler _collectionChangedHandler; + + readonly Dictionary _gestureRecognizers = new Dictionary (); + + readonly IVisualElementRenderer _renderer; + bool _disposed; + NativeView _handler; + + public EventTracker (IVisualElementRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException (nameof (renderer)); + + _collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged; + + _renderer = renderer; + _renderer.ElementChanged += OnElementChanged; + } + + ObservableCollection ElementGestureRecognizers { + get { + if (_renderer?.Element is View) + return ((View)_renderer.Element).GestureRecognizers as ObservableCollection; + return null; + } + } + + public void Dispose () + { + if (_disposed) + return; + + _disposed = true; + + foreach (var kvp in _gestureRecognizers) { + RemoveGestureRecognizer (_handler, kvp.Value); + kvp.Value.Dispose (); + } + + _gestureRecognizers.Clear (); + + if (ElementGestureRecognizers != null) + ElementGestureRecognizers.CollectionChanged -= _collectionChangedHandler; + + _handler = null; + } + + void ModelGestureRecognizersOnCollectionChanged (object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + LoadRecognizers (); + } + + void OnElementChanged (object sender, VisualElementChangedEventArgs e) + { + if (e.OldElement != null) { + // unhook + var oldView = e.OldElement as View; + if (oldView != null) { + var oldRecognizers = (ObservableCollection)oldView.GestureRecognizers; + oldRecognizers.CollectionChanged -= _collectionChangedHandler; + } + } + + if (e.NewElement != null) { + // hook + if (ElementGestureRecognizers != null) { + ElementGestureRecognizers.CollectionChanged += _collectionChangedHandler; + LoadRecognizers (); + } + } + } + + void LoadRecognizers () + { + if (ElementGestureRecognizers == null) + return; + + foreach (var recognizer in ElementGestureRecognizers) { + if (_gestureRecognizers.ContainsKey (recognizer)) + continue; + + var nativeRecognizer = GetNativeRecognizer (recognizer); + if (nativeRecognizer != null) { + AddGestureRecognizer (_handler, nativeRecognizer); + + _gestureRecognizers[recognizer] = nativeRecognizer; + } + } + + var toRemove = _gestureRecognizers.Keys.Where (key => !ElementGestureRecognizers.Contains (key)).ToArray (); + foreach (var gestureRecognizer in toRemove) { + var uiRecognizer = _gestureRecognizers[gestureRecognizer]; + _gestureRecognizers.Remove (gestureRecognizer); + + RemoveGestureRecognizer (_handler, uiRecognizer); + uiRecognizer.Dispose (); + } + } + + protected virtual NativeGestureRecognizer GetNativeRecognizer (IGestureRecognizer recognizer) + { + if (recognizer == null) + return null; + + var weakRecognizer = new WeakReference (recognizer); + var weakEventTracker = new WeakReference (this); + + var tapRecognizer = recognizer as TapGestureRecognizer; + if (tapRecognizer != null) { + var returnAction = new TargetEventHandler ((s, e) => { + var tapGestureRecognizer = weakRecognizer.Target as TapGestureRecognizer; + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + if (tapGestureRecognizer != null && view != null) + tapGestureRecognizer.SendTapped (view); + }); + var uiRecognizer = new NativeGestureRecognizer { + EventType = "click", + Handler = returnAction, + }; + return uiRecognizer; + } + + return null; + } + + static void AddGestureRecognizer (Element element, NativeGestureRecognizer recognizer) + { + element.AddEventListener (recognizer.EventType, recognizer.Handler); + } + + static void RemoveGestureRecognizer (Element element, NativeGestureRecognizer recognizer) + { + element.RemoveEventListener (recognizer.EventType, recognizer.Handler); + } + + public void LoadEvents (NativeView handler) + { + if (_disposed) + throw new ObjectDisposedException (null); + + _handler = handler; + OnElementChanged (this, new VisualElementChangedEventArgs (null, _renderer.Element)); + } + + protected class NativeGestureRecognizer : IDisposable + { + public string EventType; + public TargetEventHandler Handler; + public void Dispose () + { + } + } + } +} diff --git a/Ooui.Forms/Platform.cs b/Ooui.Forms/Platform.cs index 938adb2..96197cb 100644 --- a/Ooui.Forms/Platform.cs +++ b/Ooui.Forms/Platform.cs @@ -57,6 +57,10 @@ namespace Ooui.Forms MessagingCenter.Unsubscribe (this, Page.ActionSheetSignalName); MessagingCenter.Unsubscribe (this, Page.AlertSignalName); MessagingCenter.Unsubscribe (this, Page.BusySetSignalName); + + DisposeModelAndChildrenRenderers (Page); + //foreach (var modal in _modals) + //DisposeModelAndChildrenRenderers (modal); } public static IVisualElementRenderer CreateRenderer (VisualElement element) @@ -110,7 +114,29 @@ namespace Ooui.Forms void HandleChildRemoved (object sender, ElementEventArgs e) { - throw new NotImplementedException (); + var view = e.Element; + DisposeModelAndChildrenRenderers (view); + } + + void DisposeModelAndChildrenRenderers (Xamarin.Forms.Element view) + { + IVisualElementRenderer renderer; + foreach (VisualElement child in view.Descendants ()) { + renderer = GetRenderer (child); + child.ClearValue (RendererProperty); + + if (renderer != null) { + //renderer.NativeView.RemoveFromSuperview (); + renderer.Dispose (); + } + } + + renderer = GetRenderer ((VisualElement)view); + if (renderer != null) { + //renderer.NativeView.RemoveFromSuperview (); + renderer.Dispose (); + } + view.ClearValue (RendererProperty); } void AddChild (VisualElement view) diff --git a/Ooui.Forms/VisualElementRenderer.cs b/Ooui.Forms/VisualElementRenderer.cs index a9f9af1..f6f4548 100644 --- a/Ooui.Forms/VisualElementRenderer.cs +++ b/Ooui.Forms/VisualElementRenderer.cs @@ -38,6 +38,7 @@ namespace Ooui.Forms VisualElementRendererFlags _flags = VisualElementRendererFlags.AutoPackage | VisualElementRendererFlags.AutoTrack; + EventTracker _events; VisualElementPackager _packager; VisualElementTracker _tracker; @@ -107,10 +108,10 @@ namespace Ooui.Forms _packager.Load (); } - //if (AutoTrack && _events == null) { - // _events = new EventTracker (this); - // _events.LoadEvents (this); - //} + if (AutoTrack && _events == null) { + _events = new EventTracker (this); + _events.LoadEvents (this); + } element.PropertyChanged += _propertyChangedHandler; }