Browse Source

Enable NRT in Avalonia.Interactivity.

pull/3621/head
Steven Kirk 7 years ago
parent
commit
b966bd390c
  1. 6
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  2. 20
      src/Avalonia.Interactivity/EventSubscription.cs
  3. 2
      src/Avalonia.Interactivity/IInteractive.cs
  4. 68
      src/Avalonia.Interactivity/Interactive.cs
  5. 5
      src/Avalonia.Interactivity/InteractiveExtensions.cs
  6. 18
      src/Avalonia.Interactivity/RoutedEvent.cs
  7. 8
      src/Avalonia.Interactivity/RoutedEventArgs.cs
  8. 6
      src/Avalonia.Interactivity/RoutedEventRegistry.cs

6
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Nullable>Enable</Nullable>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -9,6 +11,4 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
</Project>
</Project>

20
src/Avalonia.Interactivity/EventSubscription.cs

@ -9,12 +9,24 @@ namespace Avalonia.Interactivity
internal class EventSubscription
{
public HandlerInvokeSignature InvokeAdapter { get; set; }
public EventSubscription(
Delegate handler,
RoutingStrategies routes,
bool handledEventsToo,
HandlerInvokeSignature? invokeAdapter = null)
{
Handler = handler;
Routes = routes;
HandledEventsToo = handledEventsToo;
InvokeAdapter = invokeAdapter;
}
public Delegate Handler { get; set; }
public HandlerInvokeSignature? InvokeAdapter { get; }
public RoutingStrategies Routes { get; set; }
public Delegate Handler { get; }
public bool AlsoIfHandled { get; set; }
public RoutingStrategies Routes { get; }
public bool HandledEventsToo { get; }
}
}

2
src/Avalonia.Interactivity/IInteractive.cs

@ -13,7 +13,7 @@ namespace Avalonia.Interactivity
/// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary>
IInteractive InteractiveParent { get; }
IInteractive? InteractiveParent { get; }
/// <summary>
/// Adds a handler for the specified routed event.

68
src/Avalonia.Interactivity/Interactive.cs

@ -15,16 +15,16 @@ namespace Avalonia.Interactivity
/// </summary>
public class Interactive : Layoutable, IInteractive
{
private Dictionary<RoutedEvent, List<EventSubscription>> _eventHandlers;
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;
IInteractive? IInteractive.InteractiveParent => ((IVisual)this).VisualParent as IInteractive;
private Dictionary<RoutedEvent, List<EventSubscription>> EventHandlers => _eventHandlers ?? (_eventHandlers = new Dictionary<RoutedEvent, List<EventSubscription>>());
private Dictionary<RoutedEvent, List<EventSubscription>> EventHandlers => _eventHandlers ??= new Dictionary<RoutedEvent, List<EventSubscription>>();
/// <summary>
/// Adds a handler for the specified routed event.
@ -40,16 +40,10 @@ namespace Avalonia.Interactivity
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false)
{
Contract.Requires<ArgumentNullException>(routedEvent != null);
Contract.Requires<ArgumentNullException>(handler != null);
var subscription = new EventSubscription
{
Handler = handler,
Routes = routes,
AlsoIfHandled = handledEventsToo,
};
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
handler = handler ?? throw new ArgumentNullException(nameof(handler));
var subscription = new EventSubscription(handler, routes, handledEventsToo);
return AddEventSubscription(routedEvent, subscription);
}
@ -68,12 +62,12 @@ namespace Avalonia.Interactivity
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
{
Contract.Requires<ArgumentNullException>(routedEvent != null);
Contract.Requires<ArgumentNullException>(handler != null);
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
handler = handler ?? throw new ArgumentNullException(nameof(handler));
// 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;
var eventArgsType = routedEvent.EventArgsType;
if (!s_invokeHandlerCache.TryGetValue(eventArgsType, out var invokeAdapter))
{
@ -90,14 +84,7 @@ namespace Avalonia.Interactivity
s_invokeHandlerCache.Add(eventArgsType, invokeAdapter);
}
var subscription = new EventSubscription
{
InvokeAdapter = invokeAdapter,
Handler = handler,
Routes = routes,
AlsoIfHandled = handledEventsToo,
};
var subscription = new EventSubscription(handler, routes, handledEventsToo, invokeAdapter);
return AddEventSubscription(routedEvent, subscription);
}
@ -108,12 +95,11 @@ namespace Avalonia.Interactivity
/// <param name="handler">The handler.</param>
public void RemoveHandler(RoutedEvent routedEvent, Delegate handler)
{
Contract.Requires<ArgumentNullException>(routedEvent != null);
Contract.Requires<ArgumentNullException>(handler != null);
List<EventSubscription> subscriptions = null;
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
handler = handler ?? throw new ArgumentNullException(nameof(handler));
if (_eventHandlers?.TryGetValue(routedEvent, out subscriptions) == true)
if (_eventHandlers is object &&
_eventHandlers.TryGetValue(routedEvent, out var subscriptions) == true)
{
subscriptions.RemoveAll(x => x.Handler == handler);
}
@ -137,9 +123,14 @@ namespace Avalonia.Interactivity
/// <param name="e">The event args.</param>
public void RaiseEvent(RoutedEventArgs e)
{
Contract.Requires<ArgumentNullException>(e != null);
e = e ?? throw new ArgumentNullException(nameof(e));
e.Source = e.Source ?? this;
if (e.RoutedEvent == null)
{
throw new ArgumentException("Cannot raise an event whose RoutedEvent is null.");
}
e.Source ??= this;
if (e.RoutedEvent.RoutingStrategies == RoutingStrategies.Direct)
{
@ -167,7 +158,7 @@ namespace Avalonia.Interactivity
/// <param name="e">The event args.</param>
private void BubbleEvent(RoutedEventArgs e)
{
Contract.Requires<ArgumentNullException>(e != null);
e = e ?? throw new ArgumentNullException(nameof(e));
e.Route = RoutingStrategies.Bubble;
@ -182,7 +173,7 @@ namespace Avalonia.Interactivity
/// <param name="e">The event args.</param>
private void TunnelEvent(RoutedEventArgs e)
{
Contract.Requires<ArgumentNullException>(e != null);
e = e ?? throw new ArgumentNullException(nameof(e));
e.Route = RoutingStrategies.Tunnel;
@ -197,18 +188,17 @@ namespace Avalonia.Interactivity
/// <param name="e">The event args.</param>
private void RaiseEventImpl(RoutedEventArgs e)
{
Contract.Requires<ArgumentNullException>(e != null);
e.RoutedEvent.InvokeRaised(this, e);
e = e ?? throw new ArgumentNullException(nameof(e));
List<EventSubscription> subscriptions = null;
e.RoutedEvent!.InvokeRaised(this, e);
if (_eventHandlers?.TryGetValue(e.RoutedEvent, out subscriptions) == true)
if (_eventHandlers is object &&
_eventHandlers.TryGetValue(e.RoutedEvent, out var subscriptions) == true)
{
foreach (var sub in subscriptions.ToList())
{
bool correctRoute = (e.Route & sub.Routes) != 0;
bool notFinished = !e.Handled || sub.AlsoIfHandled;
bool notFinished = !e.Handled || sub.HandledEventsToo;
if (correctRoute && notFinished)
{
@ -313,7 +303,7 @@ namespace Avalonia.Interactivity
{
_preTraverse.Execute(target, _args);
IInteractive parent = target.InteractiveParent;
var parent = target.InteractiveParent;
if (parent != null)
{

5
src/Avalonia.Interactivity/InteractiveExtensions.cs

@ -2,8 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace Avalonia.Interactivity
@ -30,6 +28,9 @@ namespace Avalonia.Interactivity
bool handledEventsToo = false)
where TEventArgs : RoutedEventArgs
{
o = o ?? throw new ArgumentNullException(nameof(o));
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
return Observable.Create<TEventArgs>(x => o.AddHandler(
routedEvent,
(_, e) => x.OnNext(e),

18
src/Avalonia.Interactivity/RoutedEvent.cs

@ -25,10 +25,14 @@ namespace Avalonia.Interactivity
Type eventArgsType,
Type ownerType)
{
Contract.Requires<ArgumentNullException>(name != null);
Contract.Requires<ArgumentNullException>(eventArgsType != null);
Contract.Requires<ArgumentNullException>(ownerType != null);
Contract.Requires<InvalidCastException>(typeof(RoutedEventArgs).IsAssignableFrom(eventArgsType));
name = name ?? throw new ArgumentNullException(nameof(name));
eventArgsType = eventArgsType ?? throw new ArgumentNullException(nameof(name));
ownerType = ownerType ?? throw new ArgumentNullException(nameof(name));
if (!typeof(RoutedEventArgs).IsAssignableFrom(eventArgsType))
{
throw new InvalidCastException("eventArgsType must be derived from RoutedEventArgs.");
}
EventArgsType = eventArgsType;
Name = name;
@ -52,7 +56,7 @@ namespace Avalonia.Interactivity
RoutingStrategies routingStrategy)
where TEventArgs : RoutedEventArgs
{
Contract.Requires<ArgumentNullException>(name != null);
name = name ?? throw new ArgumentNullException(nameof(name));
var routedEvent = new RoutedEvent<TEventArgs>(name, routingStrategy, typeof(TOwner));
RoutedEventRegistry.Instance.Register(typeof(TOwner), routedEvent);
@ -65,7 +69,7 @@ namespace Avalonia.Interactivity
Type ownerType)
where TEventArgs : RoutedEventArgs
{
Contract.Requires<ArgumentNullException>(name != null);
name = name ?? throw new ArgumentNullException(nameof(name));
var routedEvent = new RoutedEvent<TEventArgs>(name, routingStrategy, ownerType);
RoutedEventRegistry.Instance.Register(ownerType, routedEvent);
@ -108,8 +112,6 @@ namespace Avalonia.Interactivity
public RoutedEvent(string name, RoutingStrategies routingStrategies, Type ownerType)
: base(name, routingStrategies, typeof(TEventArgs), ownerType)
{
Contract.Requires<ArgumentNullException>(name != null);
Contract.Requires<ArgumentNullException>(ownerType != null);
}
[Obsolete("Use overload taking Action<TTarget, TEventArgs>.")]

8
src/Avalonia.Interactivity/RoutedEventArgs.cs

@ -11,12 +11,12 @@ namespace Avalonia.Interactivity
{
}
public RoutedEventArgs(RoutedEvent routedEvent)
public RoutedEventArgs(RoutedEvent? routedEvent)
{
RoutedEvent = routedEvent;
}
public RoutedEventArgs(RoutedEvent routedEvent, IInteractive source)
public RoutedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
{
RoutedEvent = routedEvent;
Source = source;
@ -24,10 +24,10 @@ namespace Avalonia.Interactivity
public bool Handled { get; set; }
public RoutedEvent RoutedEvent { get; set; }
public RoutedEvent? RoutedEvent { get; set; }
public RoutingStrategies Route { get; set; }
public IInteractive Source { get; set; }
public IInteractive? Source { get; set; }
}
}

6
src/Avalonia.Interactivity/RoutedEventRegistry.cs

@ -32,8 +32,8 @@ namespace Avalonia.Interactivity
/// </remarks>
public void Register(Type type, RoutedEvent @event)
{
Contract.Requires<ArgumentNullException>(type != null);
Contract.Requires<ArgumentNullException>(@event != null);
type = type ?? throw new ArgumentNullException(nameof(type));
@event = @event ?? throw new ArgumentNullException(nameof(@event));
if (!_registeredRoutedEvents.TryGetValue(type, out var list))
{
@ -66,7 +66,7 @@ namespace Avalonia.Interactivity
/// <returns>All routed events registered with the provided type.</returns>
public IReadOnlyList<RoutedEvent> GetRegistered(Type type)
{
Contract.Requires<ArgumentNullException>(type != null);
type = type ?? throw new ArgumentNullException(nameof(type));
if (_registeredRoutedEvents.TryGetValue(type, out var events))
{

Loading…
Cancel
Save