Browse Source

Merge branch 'master' into prioritybinding-fixes

Dariusz Komosiński 6 years ago
parent
commit
65a29fff79

+ 1 - 1
src/Avalonia.Input/Gestures.cs

@@ -31,7 +31,7 @@ namespace Avalonia.Input
             RoutedEvent.Register<ScrollGestureEventArgs>(
                 "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
 
-        private static WeakReference<IInteractive> s_lastPress;
+        private static WeakReference<IInteractive> s_lastPress = new WeakReference<IInteractive>(null);
 
         static Gestures()
         {

+ 4 - 0
src/Avalonia.Interactivity/EventSubscription.cs

@@ -5,8 +5,12 @@ using System;
 
 namespace Avalonia.Interactivity
 {
+    internal delegate void HandlerInvokeSignature(Delegate baseHandler, object sender, RoutedEventArgs args);
+
     internal class EventSubscription
     {
+        public HandlerInvokeSignature InvokeAdapter { get; set; }
+
         public Delegate Handler { get; set; }
 
         public RoutingStrategies Routes { get; set; }

+ 81 - 20
src/Avalonia.Interactivity/Interactive.cs

@@ -4,8 +4,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
 using Avalonia.Layout;
 using Avalonia.VisualTree;
 
@@ -18,15 +16,14 @@ namespace Avalonia.Interactivity
     {
         private Dictionary<RoutedEvent, List<EventSubscription>> _eventHandlers;
 
+        private static readonly Dictionary<Type, HandlerInvokeSignature> s_invokeHandlerCache = new Dictionary<Type, HandlerInvokeSignature>();
+
         /// <summary>
         /// Gets the interactive parent of the object for bubbling and tunneling events.
         /// </summary>
         IInteractive IInteractive.InteractiveParent => ((IVisual)this).VisualParent as IInteractive;
 
-        private Dictionary<RoutedEvent, List<EventSubscription>> EventHandlers
-        {
-            get { return _eventHandlers ?? (_eventHandlers = new Dictionary<RoutedEvent, List<EventSubscription>>()); }
-        }
+        private Dictionary<RoutedEvent, List<EventSubscription>> EventHandlers => _eventHandlers ?? (_eventHandlers = new Dictionary<RoutedEvent, List<EventSubscription>>());
 
         /// <summary>
         /// Adds a handler for the specified routed event.
@@ -45,24 +42,14 @@ namespace Avalonia.Interactivity
             Contract.Requires<ArgumentNullException>(routedEvent != null);
             Contract.Requires<ArgumentNullException>(handler != null);
 
-            List<EventSubscription> subscriptions;
-
-            if (!EventHandlers.TryGetValue(routedEvent, out subscriptions))
-            {
-                subscriptions = new List<EventSubscription>();
-                EventHandlers.Add(routedEvent, subscriptions);
-            }
-
-            var sub = new EventSubscription
+            var subscription = new EventSubscription
             {
                 Handler = handler,
                 Routes = routes,
                 AlsoIfHandled = handledEventsToo,
             };
 
-            subscriptions.Add(sub);
-
-            return Disposable.Create(() => subscriptions.Remove(sub));
+            return AddEventSubscription(routedEvent, subscription);
         }
 
         /// <summary>
@@ -80,7 +67,37 @@ namespace Avalonia.Interactivity
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
         {
-            return AddHandler(routedEvent, (Delegate)handler, routes, handledEventsToo);
+            Contract.Requires<ArgumentNullException>(routedEvent != null);
+            Contract.Requires<ArgumentNullException>(handler != null);
+
+            // EventHandler delegate is not covariant, this forces us to create small wrapper
+            // that will cast our type erased instance and invoke it.
+            Type eventArgsType = routedEvent.EventArgsType;
+
+            if (!s_invokeHandlerCache.TryGetValue(eventArgsType, out var invokeAdapter))
+            {
+                void InvokeAdapter(Delegate baseHandler, object sender, RoutedEventArgs args)
+                {
+                    var typedHandler = (EventHandler<TEventArgs>)baseHandler;
+                    var typedArgs = (TEventArgs)args;
+
+                    typedHandler(sender, typedArgs);
+                }
+
+                invokeAdapter = InvokeAdapter;
+
+                s_invokeHandlerCache.Add(eventArgsType, invokeAdapter);
+            }
+
+            var subscription = new EventSubscription
+            {
+                InvokeAdapter = invokeAdapter,
+                Handler = handler,
+                Routes = routes,
+                AlsoIfHandled = handledEventsToo,
+            };
+
+            return AddEventSubscription(routedEvent, subscription);
         }
 
         /// <summary>
@@ -196,10 +213,54 @@ namespace Avalonia.Interactivity
 
                     if (correctRoute && notFinished)
                     {
-                        sub.Handler.DynamicInvoke(this, e);
+                        if (sub.InvokeAdapter != null)
+                        {
+                            sub.InvokeAdapter(sub.Handler, this, e);
+                        }
+                        else
+                        {
+                            sub.Handler.DynamicInvoke(this, e);
+                        }
                     }
                 }
             }
         }
+
+        private List<EventSubscription> GetEventSubscriptions(RoutedEvent routedEvent)
+        {
+            if (!EventHandlers.TryGetValue(routedEvent, out var subscriptions))
+            {
+                subscriptions = new List<EventSubscription>();
+                EventHandlers.Add(routedEvent, subscriptions);
+            }
+
+            return subscriptions;
+        }
+
+        private IDisposable AddEventSubscription(RoutedEvent routedEvent, EventSubscription subscription)
+        {
+            List<EventSubscription> subscriptions = GetEventSubscriptions(routedEvent);
+
+            subscriptions.Add(subscription);
+
+            return new UnsubscribeDisposable(subscriptions, subscription);
+        }
+
+        private sealed class UnsubscribeDisposable : IDisposable
+        {
+            private readonly List<EventSubscription> _subscriptions;
+            private readonly EventSubscription _subscription;
+
+            public UnsubscribeDisposable(List<EventSubscription> subscriptions, EventSubscription subscription)
+            {
+                _subscriptions = subscriptions;
+                _subscription = subscription;
+            }
+
+            public void Dispose()
+            {
+                _subscriptions.Remove(_subscription);
+            }
+        }
     }
 }

+ 12 - 38
src/Avalonia.Interactivity/RoutedEvent.cs

@@ -4,7 +4,6 @@
 using System;
 using System.Reactive.Subjects;
 using System.Reflection;
-using System.Runtime.ExceptionServices;
 
 namespace Avalonia.Interactivity
 {
@@ -18,8 +17,8 @@ namespace Avalonia.Interactivity
 
     public class RoutedEvent
     {
-        private Subject<Tuple<object, RoutedEventArgs>> _raised = new Subject<Tuple<object, RoutedEventArgs>>();
-        private Subject<RoutedEventArgs> _routeFinished = new Subject<RoutedEventArgs>();
+        private readonly Subject<(object, RoutedEventArgs)> _raised = new Subject<(object, RoutedEventArgs)>();
+        private readonly Subject<RoutedEventArgs> _routeFinished = new Subject<RoutedEventArgs>();
 
         public RoutedEvent(
             string name,
@@ -38,31 +37,15 @@ namespace Avalonia.Interactivity
             RoutingStrategies = routingStrategies;
         }
 
-        public Type EventArgsType
-        {
-            get;
-            private set;
-        }
+        public Type EventArgsType { get; }
 
-        public string Name
-        {
-            get;
-            private set;
-        }
+        public string Name { get; }
 
-        public Type OwnerType
-        {
-            get;
-            private set;
-        }
+        public Type OwnerType { get; }
 
-        public RoutingStrategies RoutingStrategies
-        {
-            get;
-            private set;
-        }
+        public RoutingStrategies RoutingStrategies { get; }
 
-        public IObservable<Tuple<object, RoutedEventArgs>> Raised => _raised;
+        public IObservable<(object, RoutedEventArgs)> Raised => _raised;
         public IObservable<RoutedEventArgs> RouteFinished => _routeFinished;
 
         public static RoutedEvent<TEventArgs> Register<TOwner, TEventArgs>(
@@ -98,29 +81,20 @@ namespace Avalonia.Interactivity
         {
             return Raised.Subscribe(args =>
             {
-                var sender = args.Item1;
-                var e = args.Item2;
+                (object sender, RoutedEventArgs e) = args;
 
-                if (targetType.GetTypeInfo().IsAssignableFrom(sender.GetType().GetTypeInfo()) &&
-                    ((e.Route == RoutingStrategies.Direct) || (e.Route & routes) != 0) &&
+                if (targetType.IsInstanceOfType(sender) &&
+                    (e.Route == RoutingStrategies.Direct || (e.Route & routes) != 0) &&
                     (!e.Handled || handledEventsToo))
                 {
-                    try
-                    {
-                        handler.DynamicInvoke(sender, e);
-                    }
-                    catch (TargetInvocationException ex)
-                    {
-                        // Unwrap the inner exception.
-                        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
-                    }
+                    handler(sender, e);
                 }
             });
         }
 
         internal void InvokeRaised(object sender, RoutedEventArgs e)
         {
-            _raised.OnNext(Tuple.Create(sender, e));
+            _raised.OnNext((sender, e));
         }
 
         internal void InvokeRouteFinished(RoutedEventArgs e)