Pārlūkot izejas kodu

Removed IInteractive.

Steven Kirk 3 gadi atpakaļ
vecāks
revīzija
8347093f90

+ 15 - 12
src/Avalonia.Base/Input/Gestures.cs

@@ -43,7 +43,7 @@ namespace Avalonia.Input
             RoutedEvent.Register<PointerDeltaEventArgs>(
             RoutedEvent.Register<PointerDeltaEventArgs>(
                 "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
                 "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
 
 
-        private static readonly WeakReference<IInteractive?> s_lastPress = new WeakReference<IInteractive?>(null);
+        private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
         private static Point s_lastPressPoint;
         private static Point s_lastPressPoint;
 
 
         static Gestures()
         static Gestures()
@@ -52,32 +52,32 @@ namespace Avalonia.Input
             InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
             InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
         }
         }
 
 
-        public static void AddTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.AddHandler(TappedEvent, handler);
             element.AddHandler(TappedEvent, handler);
         }
         }
 
 
-        public static void AddDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.AddHandler(DoubleTappedEvent, handler);
             element.AddHandler(DoubleTappedEvent, handler);
         }
         }
 
 
-        public static void AddRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.AddHandler(RightTappedEvent, handler);
             element.AddHandler(RightTappedEvent, handler);
         }
         }
 
 
-        public static void RemoveTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.RemoveHandler(TappedEvent, handler);
             element.RemoveHandler(TappedEvent, handler);
         }
         }
 
 
-        public static void RemoveDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.RemoveHandler(DoubleTappedEvent, handler);
             element.RemoveHandler(DoubleTappedEvent, handler);
         }
         }
 
 
-        public static void RemoveRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
         {
             element.RemoveHandler(RightTappedEvent, handler);
             element.RemoveHandler(RightTappedEvent, handler);
         }
         }
@@ -102,10 +102,12 @@ namespace Avalonia.Input
                 }
                 }
                 else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
                 else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
                 {
                 {
-                    if (s_lastPress.TryGetTarget(out var target) && target == e.Source)
+                    if (s_lastPress.TryGetTarget(out var target) && 
+                        target == e.Source && 
+                        e.Source is Interactive i)
                     {
                     {
                         s_isDoubleTapped = true;
                         s_isDoubleTapped = true;
-                        e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
+                        i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
                     }
                     }
                 }
                 }
             }
             }
@@ -119,7 +121,8 @@ namespace Avalonia.Input
 
 
                 if (s_lastPress.TryGetTarget(out var target) && 
                 if (s_lastPress.TryGetTarget(out var target) && 
                     target == e.Source &&
                     target == e.Source &&
-                    e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right)
+                    e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
+                    e.Source is Interactive i)
                 {
                 {
                     var point = e.GetCurrentPoint((Visual)target);
                     var point = e.GetCurrentPoint((Visual)target);
                     var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
                     var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
@@ -131,13 +134,13 @@ namespace Avalonia.Input
                     {
                     {
                         if (e.InitialPressMouseButton == MouseButton.Right)
                         if (e.InitialPressMouseButton == MouseButton.Right)
                         {
                         {
-                            e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
+                            i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
                         }
                         }
                         //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
                         //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
                         //This behaviour matches UWP behaviour.
                         //This behaviour matches UWP behaviour.
                         else if (s_isDoubleTapped == false)
                         else if (s_isDoubleTapped == false)
                         {
                         {
-                            e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e));
+                            i.RaiseEvent(new TappedEventArgs(TappedEvent, e));
                         }
                         }
                     }
                     }
                 }
                 }

+ 28 - 4
src/Avalonia.Base/Input/IInputElement.cs

@@ -2,9 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Metadata;
 using Avalonia.Metadata;
-using Avalonia.VisualTree;
-
-#nullable enable
 
 
 namespace Avalonia.Input
 namespace Avalonia.Input
 {
 {
@@ -12,7 +9,7 @@ namespace Avalonia.Input
     /// Defines input-related functionality for a control.
     /// Defines input-related functionality for a control.
     /// </summary>
     /// </summary>
     [NotClientImplementable]
     [NotClientImplementable]
-    public interface IInputElement : IInteractive
+    public interface IInputElement
     {
     {
         /// <summary>
         /// <summary>
         /// Occurs when the control receives focus.
         /// Occurs when the control receives focus.
@@ -128,5 +125,32 @@ namespace Avalonia.Input
         /// Gets the key bindings for the element.
         /// Gets the key bindings for the element.
         /// </summary>
         /// </summary>
         List<KeyBinding> KeyBindings { get; }
         List<KeyBinding> KeyBindings { get; }
+
+        /// <summary>
+        /// Adds a handler for the specified routed event.
+        /// </summary>
+        /// <param name="routedEvent">The routed event.</param>
+        /// <param name="handler">The handler.</param>
+        /// <param name="routes">The routing strategies to listen to.</param>
+        /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
+        /// <returns>A disposable that terminates the event subscription.</returns>
+        void AddHandler(
+            RoutedEvent routedEvent,
+            Delegate handler,
+            RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
+            bool handledEventsToo = false);
+
+        /// <summary>
+        /// Removes a handler for the specified routed event.
+        /// </summary>
+        /// <param name="routedEvent">The routed event.</param>
+        /// <param name="handler">The handler.</param>
+        void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
+
+        /// <summary>
+        /// Raises a routed event.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        void RaiseEvent(RoutedEventArgs e);
     }
     }
 }
 }

+ 2 - 2
src/Avalonia.Base/Input/KeyboardDevice.cs

@@ -133,7 +133,7 @@ namespace Avalonia.Input
         {
         {
             if (element != FocusedElement)
             if (element != FocusedElement)
             {
             {
-                var interactive = FocusedElement as IInteractive;
+                var interactive = FocusedElement as Interactive;
 
 
                 if (FocusedElement != null && 
                 if (FocusedElement != null && 
                     (!((Visual)FocusedElement).IsAttachedToVisualTree ||
                     (!((Visual)FocusedElement).IsAttachedToVisualTree ||
@@ -152,7 +152,7 @@ namespace Avalonia.Input
                     RoutedEvent = InputElement.LostFocusEvent,
                     RoutedEvent = InputElement.LostFocusEvent,
                 });
                 });
 
 
-                interactive = element as IInteractive;
+                interactive = element as Interactive;
 
 
                 interactive?.RaiseEvent(new GotFocusEventArgs
                 interactive?.RaiseEvent(new GotFocusEventArgs
                 {
                 {

+ 1 - 1
src/Avalonia.Base/Input/PointerDeltaEventArgs.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Input
     {
     {
         public Vector Delta { get; set; }
         public Vector Delta { get; set; }
 
 
-        internal PointerDeltaEventArgs(RoutedEvent routedEvent, IInteractive? source, 
+        internal PointerDeltaEventArgs(RoutedEvent routedEvent, object? source, 
             IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
             IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta) 
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta) 
             : base(routedEvent, source, pointer, rootVisual, rootVisualPosition,
             : base(routedEvent, source, pointer, rootVisual, rootVisualPosition,

+ 6 - 6
src/Avalonia.Base/Input/PointerEventArgs.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Input
         private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
         private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
 
 
         internal PointerEventArgs(RoutedEvent routedEvent,
         internal PointerEventArgs(RoutedEvent routedEvent,
-            IInteractive? source,
+            object? source,
             IPointer pointer,
             IPointer pointer,
             Visual? rootVisual, Point rootVisualPosition,
             Visual? rootVisual, Point rootVisualPosition,
             ulong timestamp,
             ulong timestamp,
@@ -30,9 +30,9 @@ namespace Avalonia.Input
             Timestamp = timestamp;
             Timestamp = timestamp;
             KeyModifiers = modifiers;
             KeyModifiers = modifiers;
         }
         }
-
+        
         internal PointerEventArgs(RoutedEvent routedEvent,
         internal PointerEventArgs(RoutedEvent routedEvent,
-            IInteractive? source,
+            object? source,
             IPointer pointer,
             IPointer pointer,
             Visual? rootVisual, Point rootVisualPosition,
             Visual? rootVisual, Point rootVisualPosition,
             ulong timestamp,
             ulong timestamp,
@@ -125,7 +125,7 @@ namespace Avalonia.Input
     public class PointerPressedEventArgs : PointerEventArgs
     public class PointerPressedEventArgs : PointerEventArgs
     {
     {
         internal PointerPressedEventArgs(
         internal PointerPressedEventArgs(
-            IInteractive source,
+            object source,
             IPointer pointer,
             IPointer pointer,
             Visual rootVisual, Point rootVisualPosition,
             Visual rootVisual, Point rootVisualPosition,
             ulong timestamp,
             ulong timestamp,
@@ -144,7 +144,7 @@ namespace Avalonia.Input
     public class PointerReleasedEventArgs : PointerEventArgs
     public class PointerReleasedEventArgs : PointerEventArgs
     {
     {
         internal PointerReleasedEventArgs(
         internal PointerReleasedEventArgs(
-            IInteractive source, IPointer pointer,
+            object source, IPointer pointer,
             Visual rootVisual, Point rootVisualPosition, ulong timestamp,
             Visual rootVisual, Point rootVisualPosition, ulong timestamp,
             PointerPointProperties properties, KeyModifiers modifiers,
             PointerPointProperties properties, KeyModifiers modifiers,
             MouseButton initialPressMouseButton)
             MouseButton initialPressMouseButton)
@@ -164,7 +164,7 @@ namespace Avalonia.Input
     {
     {
         public IPointer Pointer { get; }
         public IPointer Pointer { get; }
 
 
-        internal PointerCaptureLostEventArgs(IInteractive source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
+        internal PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
         {
         {
             Pointer = pointer;
             Pointer = pointer;
             Source = source;
             Source = source;

+ 1 - 1
src/Avalonia.Base/Input/PointerWheelEventArgs.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Input
     {
     {
         public Vector Delta { get; set; }
         public Vector Delta { get; set; }
 
 
-        internal PointerWheelEventArgs(IInteractive source, IPointer pointer, Visual rootVisual,
+        internal PointerWheelEventArgs(object source, IPointer pointer, Visual rootVisual,
             Point rootVisualPosition, ulong timestamp,
             Point rootVisualPosition, ulong timestamp,
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
             : base(InputElement.PointerWheelChangedEvent, source, pointer, rootVisual, rootVisualPosition,
             : base(InputElement.PointerWheelChangedEvent, source, pointer, rootVisual, rootVisualPosition,

+ 6 - 6
src/Avalonia.Base/Interactivity/EventRoute.cs

@@ -43,7 +43,7 @@ namespace Avalonia.Interactivity
         /// `DynamicInvoke` on the handler.
         /// `DynamicInvoke` on the handler.
         /// </param>
         /// </param>
         public void Add(
         public void Add(
-            IInteractive target,
+            Interactive target,
             Delegate handler,
             Delegate handler,
             RoutingStrategies routes,
             RoutingStrategies routes,
             bool handledEventsToo = false,
             bool handledEventsToo = false,
@@ -60,7 +60,7 @@ namespace Avalonia.Interactivity
         /// Adds a class handler to the route.
         /// Adds a class handler to the route.
         /// </summary>
         /// </summary>
         /// <param name="target">The target on which the event should be raised.</param>
         /// <param name="target">The target on which the event should be raised.</param>
-        public void AddClassHandler(IInteractive target)
+        public void AddClassHandler(Interactive target)
         {
         {
             target = target ?? throw new ArgumentNullException(nameof(target));
             target = target ?? throw new ArgumentNullException(nameof(target));
 
 
@@ -73,7 +73,7 @@ namespace Avalonia.Interactivity
         /// </summary>
         /// </summary>
         /// <param name="source">The event source.</param>
         /// <param name="source">The event source.</param>
         /// <param name="e">The event args.</param>
         /// <param name="e">The event args.</param>
-        public void RaiseEvent(IInteractive source, RoutedEventArgs e)
+        public void RaiseEvent(Interactive source, RoutedEventArgs e)
         {
         {
             source = source ?? throw new ArgumentNullException(nameof(source));
             source = source ?? throw new ArgumentNullException(nameof(source));
             e = e ?? throw new ArgumentNullException(nameof(e));
             e = e ?? throw new ArgumentNullException(nameof(e));
@@ -125,7 +125,7 @@ namespace Avalonia.Interactivity
                 throw new ArgumentException("Event source may not be null", nameof(e));
                 throw new ArgumentException("Event source may not be null", nameof(e));
             }
             }
 
 
-            IInteractive? lastTarget = null;
+            Interactive? lastTarget = null;
             var start = 0;
             var start = 0;
             var end = _route.Count;
             var end = _route.Count;
             var step = 1;
             var step = 1;
@@ -177,7 +177,7 @@ namespace Avalonia.Interactivity
         private readonly struct RouteItem
         private readonly struct RouteItem
         {
         {
             public RouteItem(
             public RouteItem(
-                IInteractive target,
+                Interactive target,
                 Delegate? handler,
                 Delegate? handler,
                 Action<Delegate, object, RoutedEventArgs>? adapter,
                 Action<Delegate, object, RoutedEventArgs>? adapter,
                 RoutingStrategies routes,
                 RoutingStrategies routes,
@@ -190,7 +190,7 @@ namespace Avalonia.Interactivity
                 HandledEventsToo = handledEventsToo;
                 HandledEventsToo = handledEventsToo;
             }
             }
 
 
-            public IInteractive Target { get; }
+            public Interactive Target { get; }
             public Delegate? Handler { get; }
             public Delegate? Handler { get; }
             public Action<Delegate, object, RoutedEventArgs>? Adapter { get; }
             public Action<Delegate, object, RoutedEventArgs>? Adapter { get; }
             public RoutingStrategies Routes { get; }
             public RoutingStrategies Routes { get; }

+ 0 - 53
src/Avalonia.Base/Interactivity/IInteractive.cs

@@ -1,53 +0,0 @@
-using System;
-using Avalonia.Metadata;
-
-#nullable enable
-
-namespace Avalonia.Interactivity
-{
-    /// <summary>
-    /// Interface for objects that raise routed events.
-    /// </summary>
-    [NotClientImplementable]
-    public interface IInteractive
-    {
-        /// <summary>
-        /// Gets the interactive parent of the object for bubbling and tunneling events.
-        /// </summary>
-        IInteractive? InteractiveParent { get; }
-
-        /// <summary>
-        /// Adds a handler for the specified routed event.
-        /// </summary>
-        /// <param name="routedEvent">The routed event.</param>
-        /// <param name="handler">The handler.</param>
-        /// <param name="routes">The routing strategies to listen to.</param>
-        /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
-        /// <returns>A disposable that terminates the event subscription.</returns>
-        void AddHandler(
-            RoutedEvent routedEvent,
-            Delegate handler,
-            RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
-            bool handledEventsToo = false);
-
-        /// <summary>
-        /// Removes a handler for the specified routed event.
-        /// </summary>
-        /// <param name="routedEvent">The routed event.</param>
-        /// <param name="handler">The handler.</param>
-        void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
-
-        /// <summary>
-        /// Adds the object's handlers for a routed event to an event route.
-        /// </summary>
-        /// <param name="routedEvent">The event.</param>
-        /// <param name="route">The event route.</param>
-        void AddToEventRoute(RoutedEvent routedEvent, EventRoute route);
-
-        /// <summary>
-        /// Raises a routed event.
-        /// </summary>
-        /// <param name="e">The event args.</param>
-        void RaiseEvent(RoutedEventArgs e);
-    }
-}

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

@@ -10,14 +10,14 @@ namespace Avalonia.Interactivity
     /// <summary>
     /// <summary>
     /// Base class for objects that raise routed events.
     /// Base class for objects that raise routed events.
     /// </summary>
     /// </summary>
-    public class Interactive : Layoutable, IInteractive
+    public class Interactive : Layoutable
     {
     {
         private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
         private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
 
 
         /// <summary>
         /// <summary>
         /// Gets the interactive parent of the object for bubbling and tunneling events.
         /// Gets the interactive parent of the object for bubbling and tunneling events.
         /// </summary>
         /// </summary>
-        IInteractive? IInteractive.InteractiveParent => this.VisualParent as IInteractive;
+        protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
 
 
         /// <summary>
         /// <summary>
         /// Adds a handler for the specified routed event.
         /// Adds a handler for the specified routed event.
@@ -125,21 +125,6 @@ namespace Avalonia.Interactivity
             route.RaiseEvent(this, e);
             route.RaiseEvent(this, e);
         }
         }
 
 
-        void IInteractive.AddToEventRoute(RoutedEvent routedEvent, EventRoute route)
-        {
-            routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
-            route = route ?? throw new ArgumentNullException(nameof(route));
-
-            if (_eventHandlers != null &&
-                _eventHandlers.TryGetValue(routedEvent, out var subscriptions))
-            {
-                foreach (var sub in subscriptions)
-                {
-                    route.Add(this, sub.Handler, sub.Routes, sub.HandledEventsToo, sub.InvokeAdapter);
-                }
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Builds an event route for a routed event.
         /// Builds an event route for a routed event.
         /// </summary>
         /// </summary>
@@ -151,7 +136,7 @@ namespace Avalonia.Interactivity
         /// and should be avoided if there are no handlers for an event. In these cases you can call
         /// and should be avoided if there are no handlers for an event. In these cases you can call
         /// this method to build the event route and check the <see cref="EventRoute.HasHandlers"/>
         /// this method to build the event route and check the <see cref="EventRoute.HasHandlers"/>
         /// property to see if there are any handlers registered on the route. If there are, call
         /// property to see if there are any handlers registered on the route. If there are, call
-        /// <see cref="EventRoute.RaiseEvent(IInteractive, RoutedEventArgs)"/> to raise the event.
+        /// <see cref="EventRoute.RaiseEvent(Interactive, RoutedEventArgs)"/> to raise the event.
         /// </remarks>
         /// </remarks>
         protected EventRoute BuildEventRoute(RoutedEvent e)
         protected EventRoute BuildEventRoute(RoutedEvent e)
         {
         {
@@ -163,7 +148,7 @@ namespace Avalonia.Interactivity
             if (e.RoutingStrategies.HasAllFlags(RoutingStrategies.Bubble) ||
             if (e.RoutingStrategies.HasAllFlags(RoutingStrategies.Bubble) ||
                 e.RoutingStrategies.HasAllFlags(RoutingStrategies.Tunnel))
                 e.RoutingStrategies.HasAllFlags(RoutingStrategies.Tunnel))
             {
             {
-                IInteractive? element = this;
+                Interactive? element = this;
 
 
                 while (element != null)
                 while (element != null)
                 {
                 {
@@ -183,7 +168,7 @@ namespace Avalonia.Interactivity
                     result.AddClassHandler(this);
                     result.AddClassHandler(this);
                 }
                 }
 
 
-                ((IInteractive)this).AddToEventRoute(e, result);
+                AddToEventRoute(e, result);
             }
             }
 
 
             return result;
             return result;
@@ -202,6 +187,21 @@ namespace Avalonia.Interactivity
             subscriptions.Add(subscription);
             subscriptions.Add(subscription);
         }
         }
 
 
+        private void AddToEventRoute(RoutedEvent routedEvent, EventRoute route)
+        {
+            routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
+            route = route ?? throw new ArgumentNullException(nameof(route));
+
+            if (_eventHandlers != null &&
+                _eventHandlers.TryGetValue(routedEvent, out var subscriptions))
+            {
+                foreach (var sub in subscriptions)
+                {
+                    route.Add(this, sub.Handler, sub.Routes, sub.HandledEventsToo, sub.InvokeAdapter);
+                }
+            }
+        }
+
         private readonly struct EventSubscription
         private readonly struct EventSubscription
         {
         {
             public EventSubscription(
             public EventSubscription(

+ 5 - 3
src/Avalonia.Base/Interactivity/InteractiveExtensions.cs

@@ -5,7 +5,7 @@ using System.Reactive.Linq;
 namespace Avalonia.Interactivity
 namespace Avalonia.Interactivity
 {
 {
     /// <summary>
     /// <summary>
-    /// Provides extension methods for the <see cref="IInteractive"/> interface.
+    /// Provides extension methods for the <see cref="Interactive"/> interface.
     /// </summary>
     /// </summary>
     public static class InteractiveExtensions
     public static class InteractiveExtensions
     {
     {
@@ -19,7 +19,7 @@ namespace Avalonia.Interactivity
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="routes">The routing strategies to listen to.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
         /// <returns>A disposable that terminates the event subscription.</returns>
         /// <returns>A disposable that terminates the event subscription.</returns>
-        public static IDisposable AddDisposableHandler<TEventArgs>(this IInteractive o, RoutedEvent<TEventArgs> routedEvent,
+        public static IDisposable AddDisposableHandler<TEventArgs>(this Interactive o, RoutedEvent<TEventArgs> routedEvent,
             EventHandler<TEventArgs> handler,
             EventHandler<TEventArgs> handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
             bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
@@ -31,6 +31,8 @@ namespace Avalonia.Interactivity
                 state => state.instance.RemoveHandler(state.routedEvent, state.handler));
                 state => state.instance.RemoveHandler(state.routedEvent, state.handler));
         }
         }
 
 
+        public static Interactive? GetInteractiveParent(this Interactive o) => o.InteractiveParent;
+
         /// <summary>
         /// <summary>
         /// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
         /// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
         /// </summary>
         /// </summary>
@@ -42,7 +44,7 @@ namespace Avalonia.Interactivity
         /// An observable which fires each time the event is raised.
         /// An observable which fires each time the event is raised.
         /// </returns>
         /// </returns>
         public static IObservable<TEventArgs> GetObservable<TEventArgs>(
         public static IObservable<TEventArgs> GetObservable<TEventArgs>(
-            this IInteractive o,
+            this Interactive o,
             RoutedEvent<TEventArgs> routedEvent,
             RoutedEvent<TEventArgs> routedEvent,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             bool handledEventsToo = false)
             bool handledEventsToo = false)

+ 1 - 1
src/Avalonia.Base/Interactivity/RoutedEvent.cs

@@ -116,7 +116,7 @@ namespace Avalonia.Interactivity
         public IDisposable AddClassHandler<TTarget>(
         public IDisposable AddClassHandler<TTarget>(
             Action<TTarget, TEventArgs> handler,
             Action<TTarget, TEventArgs> handler,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
-            bool handledEventsToo = false) where TTarget : class, IInteractive
+            bool handledEventsToo = false) where TTarget : Interactive
         {
         {
             void Adapter(object? sender, RoutedEventArgs e)
             void Adapter(object? sender, RoutedEventArgs e)
             {
             {

+ 2 - 2
src/Avalonia.Base/Interactivity/RoutedEventArgs.cs

@@ -28,7 +28,7 @@ namespace Avalonia.Interactivity
         /// </summary>
         /// </summary>
         /// <param name="routedEvent">The routed event associated with these event args.</param>
         /// <param name="routedEvent">The routed event associated with these event args.</param>
         /// <param name="source">The source object that raised the routed event.</param>
         /// <param name="source">The source object that raised the routed event.</param>
-        public RoutedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
+        public RoutedEventArgs(RoutedEvent? routedEvent, object? source)
         {
         {
             RoutedEvent = routedEvent;
             RoutedEvent = routedEvent;
             Source = source;
             Source = source;
@@ -55,6 +55,6 @@ namespace Avalonia.Interactivity
         /// <summary>
         /// <summary>
         /// Gets or sets the source object that raised the routed event.
         /// Gets or sets the source object that raised the routed event.
         /// </summary>
         /// </summary>
-        public IInteractive? Source { get; set; }
+        public object? Source { get; set; }
     }
     }
 }
 }

+ 3 - 4
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@@ -9,7 +9,7 @@ using Avalonia.VisualTree;
 
 
 namespace Avalonia.Controls.Primitives
 namespace Avalonia.Controls.Primitives
 {
 {
-    public class OverlayPopupHost : ContentControl, IPopupHost, IInteractive, IManagedPopupPositionerPopup
+    public class OverlayPopupHost : ContentControl, IPopupHost, IManagedPopupPositionerPopup
     {
     {
         /// <summary>
         /// <summary>
         /// Defines the <see cref="Transform"/> property.
         /// Defines the <see cref="Transform"/> property.
@@ -42,15 +42,14 @@ namespace Avalonia.Controls.Primitives
             set => SetValue(TransformProperty, value);
             set => SetValue(TransformProperty, value);
         }
         }
 
 
-        /// <inheritdoc/>
-        IInteractive? IInteractive.InteractiveParent => Parent;
-
         bool IPopupHost.Topmost
         bool IPopupHost.Topmost
         {
         {
             get => false;
             get => false;
             set { /* Not currently supported in overlay popups */ }
             set { /* Not currently supported in overlay popups */ }
         }
         }
 
 
+        protected internal override Interactive? InteractiveParent => Parent;
+
         public void Dispose() => Hide();
         public void Dispose() => Hide();
 
 
 
 

+ 2 - 2
src/Avalonia.Controls/Primitives/PopupRoot.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives
     /// <summary>
     /// <summary>
     /// The root window of a <see cref="Popup"/>.
     /// The root window of a <see cref="Popup"/>.
     /// </summary>
     /// </summary>
-    public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
+    public sealed class PopupRoot : WindowBase, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
     {
     {
         /// <summary>
         /// <summary>
         /// Defines the <see cref="Transform"/> property.
         /// Defines the <see cref="Transform"/> property.
@@ -72,7 +72,7 @@ namespace Avalonia.Controls.Primitives
         /// <remarks>
         /// <remarks>
         /// Popup events are passed to their parent window. This facilitates this.
         /// Popup events are passed to their parent window. This facilitates this.
         /// </remarks>
         /// </remarks>
-        IInteractive? IInteractive.InteractiveParent => Parent;
+        protected internal override Interactive? InteractiveParent => Parent;
 
 
         /// <summary>
         /// <summary>
         /// Gets the control that is hosting the popup root.
         /// Gets the control that is hosting the popup root.

+ 2 - 2
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -386,7 +386,7 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         /// </summary>
         /// <param name="eventSource">The control that raised the event.</param>
         /// <param name="eventSource">The control that raised the event.</param>
         /// <returns>The container or null if the event did not originate in a container.</returns>
         /// <returns>The container or null if the event did not originate in a container.</returns>
-        protected Control? GetContainerFromEventSource(IInteractive? eventSource)
+        protected Control? GetContainerFromEventSource(object? eventSource)
         {
         {
             for (var current = eventSource as Visual; current != null; current = current.VisualParent)
             for (var current = eventSource as Visual; current != null; current = current.VisualParent)
             {
             {
@@ -780,7 +780,7 @@ namespace Avalonia.Controls.Primitives
         /// false.
         /// false.
         /// </returns>
         /// </returns>
         protected bool UpdateSelectionFromEventSource(
         protected bool UpdateSelectionFromEventSource(
-            IInteractive? eventSource,
+            object? eventSource,
             bool select = true,
             bool select = true,
             bool rangeModifier = false,
             bool rangeModifier = false,
             bool toggleModifier = false,
             bool toggleModifier = false,

+ 2 - 2
src/Avalonia.Controls/SizeChangedEventArgs.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         /// <param name="routedEvent">The routed event associated with these event args.</param>
         /// <param name="routedEvent">The routed event associated with these event args.</param>
         /// <param name="source">The source object that raised the routed event.</param>
         /// <param name="source">The source object that raised the routed event.</param>
-        public SizeChangedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
+        public SizeChangedEventArgs(RoutedEvent? routedEvent, object? source)
             : base(routedEvent, source)
             : base(routedEvent, source)
         {
         {
         }
         }
@@ -37,7 +37,7 @@ namespace Avalonia.Controls
         /// <param name="newSize">The new size (or bounds) of the object.</param>
         /// <param name="newSize">The new size (or bounds) of the object.</param>
         public SizeChangedEventArgs(
         public SizeChangedEventArgs(
             RoutedEvent? routedEvent,
             RoutedEvent? routedEvent,
-            IInteractive? source,
+            object? source,
             Size previousSize,
             Size previousSize,
             Size newSize)
             Size newSize)
             : base(routedEvent, source)
             : base(routedEvent, source)

+ 1 - 1
src/Avalonia.Controls/TextChangedEventArgs.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Controls
         {
         {
         }
         }
 
 
-        public TextChangedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
+        public TextChangedEventArgs(RoutedEvent? routedEvent, Interactive? source)
             : base(routedEvent, source)
             : base(routedEvent, source)
         {
         {
         }
         }

+ 1 - 1
src/Avalonia.Controls/TextChangingEventArgs.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Controls
         {
         {
         }
         }
 
 
-        public TextChangingEventArgs(RoutedEvent? routedEvent, IInteractive? source)
+        public TextChangingEventArgs(RoutedEvent? routedEvent, Interactive? source)
             : base(routedEvent, source)
             : base(routedEvent, source)
         {
         {
         }
         }

+ 2 - 2
src/Avalonia.Controls/TreeView.cs

@@ -752,7 +752,7 @@ namespace Avalonia.Controls
         /// false.
         /// false.
         /// </returns>
         /// </returns>
         protected bool UpdateSelectionFromEventSource(
         protected bool UpdateSelectionFromEventSource(
-            IInteractive eventSource,
+            object eventSource,
             bool select = true,
             bool select = true,
             bool rangeModifier = false,
             bool rangeModifier = false,
             bool toggleModifier = false,
             bool toggleModifier = false,
@@ -774,7 +774,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         /// <param name="eventSource">The control that raised the event.</param>
         /// <param name="eventSource">The control that raised the event.</param>
         /// <returns>The container or null if the event did not originate in a container.</returns>
         /// <returns>The container or null if the event did not originate in a container.</returns>
-        protected TreeViewItem? GetContainerFromEventSource(IInteractive eventSource)
+        protected TreeViewItem? GetContainerFromEventSource(object eventSource)
         {
         {
             var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
             var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
                 .OfType<TreeViewItem>()
                 .OfType<TreeViewItem>()

+ 6 - 7
src/Avalonia.Native/AvaloniaNativeDragSource.cs

@@ -6,7 +6,6 @@ using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Platform;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Native.Interop;
 using Avalonia.Native.Interop;
-using Avalonia.Platform;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
 
 
 namespace Avalonia.Native
 namespace Avalonia.Native
@@ -19,14 +18,14 @@ namespace Avalonia.Native
         {
         {
             _factory = factory;
             _factory = factory;
         }
         }
-        
-        TopLevel FindRoot(IInteractive interactive)
+
+        private static TopLevel FindRoot(object? element)
         {
         {
-            while (interactive != null && !(interactive is Visual))
-                interactive = interactive.InteractiveParent;
-            if (interactive == null)
+            while (element is Interactive interactive && element is not Visual)
+                element = interactive.GetInteractiveParent();
+            if (element == null)
                 return null;
                 return null;
-            var visual = (Visual)interactive;
+            var visual = (Visual)element;
             return visual.GetVisualRoot() as TopLevel;
             return visual.GetVisualRoot() as TopLevel;
         }
         }
 
 

+ 1 - 1
tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs

@@ -363,7 +363,7 @@ namespace Avalonia.Base.UnitTests.Interactivity
             var invoked = new List<string>();
             var invoked = new List<string>();
             EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s).Name);
             EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s).Name);
             var parent = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
             var parent = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
-            var target = (IInteractive)parent.GetVisualChildren().Single();
+            var target = (Interactive)parent.GetVisualChildren().Single();
 
 
             EventHandler<RoutedEventArgs> removeHandler = (s, e) =>
             EventHandler<RoutedEventArgs> removeHandler = (s, e) =>
             {
             {

+ 1 - 1
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@@ -379,7 +379,7 @@ namespace Avalonia.Controls.UnitTests
             }
             }
         }
         }
 
 
-        private KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null)
+        private KeyEventArgs CreateKeyDownEvent(Key key, Interactive source = null)
         {
         {
             return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source };
             return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source };
         }
         }

+ 6 - 6
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@@ -11,15 +11,15 @@ namespace Avalonia.Controls.UnitTests.Platform
 {
 {
     public class DefaultMenuInteractionHandlerTests
     public class DefaultMenuInteractionHandlerTests
     {
     {
-        static PointerEventArgs CreateArgs(RoutedEvent ev, IInteractive source)
+        static PointerEventArgs CreateArgs(RoutedEvent ev, object source) 
             => new PointerEventArgs(ev, source, new FakePointer(), (Visual)source, default, 0, PointerPointProperties.None, default);
             => new PointerEventArgs(ev, source, new FakePointer(), (Visual)source, default, 0, PointerPointProperties.None, default);
 
 
-        static PointerPressedEventArgs CreatePressed(IInteractive source) => new PointerPressedEventArgs(source,
-            new FakePointer(), (Visual)source, default, 0, new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
+        static PointerPressedEventArgs CreatePressed(object source) => new PointerPressedEventArgs(source,
+            new FakePointer(), (Visual)source, default,0, new PointerPointProperties (RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
             default);
             default);
-
-        static PointerReleasedEventArgs CreateReleased(IInteractive source) => new PointerReleasedEventArgs(source,
-            new FakePointer(), (Visual)source, default, 0,
+        
+        static PointerReleasedEventArgs CreateReleased(object source) => new PointerReleasedEventArgs(source,
+            new FakePointer(), (Visual)source, default,0,
             new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased),
             new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonReleased),
             default, MouseButton.Left);
             default, MouseButton.Left);
 
 

+ 10 - 10
tests/Avalonia.UnitTests/MouseTestHelper.cs

@@ -38,13 +38,13 @@ namespace Avalonia.UnitTests
 
 
         private MouseButton _pressedButton;
         private MouseButton _pressedButton;
 
 
-        public void Down(IInteractive target, MouseButton mouseButton = MouseButton.Left, Point position = default,
+        public void Down(Interactive target, MouseButton mouseButton = MouseButton.Left, Point position = default,
             KeyModifiers modifiers = default, int clickCount = 1)
             KeyModifiers modifiers = default, int clickCount = 1)
         {
         {
             Down(target, target, mouseButton, position, modifiers, clickCount);
             Down(target, target, mouseButton, position, modifiers, clickCount);
         }
         }
 
 
-        public void Down(IInteractive target, IInteractive source, MouseButton mouseButton = MouseButton.Left, 
+        public void Down(Interactive target, Interactive source, MouseButton mouseButton = MouseButton.Left, 
             Point position = default, KeyModifiers modifiers = default, int clickCount = 1)
             Point position = default, KeyModifiers modifiers = default, int clickCount = 1)
         {
         {
             _pressedButtons |= Convert(mouseButton);
             _pressedButtons |= Convert(mouseButton);
@@ -64,18 +64,18 @@ namespace Avalonia.UnitTests
             }
             }
         }
         }
 
 
-        public void Move(IInteractive target, in Point position, KeyModifiers modifiers = default) => Move(target, target, position, modifiers);
-        public void Move(IInteractive target, IInteractive source, in Point position, KeyModifiers modifiers = default)
+        public void Move(Interactive target, in Point position, KeyModifiers modifiers = default) => Move(target, target, position, modifiers);
+        public void Move(Interactive target, Interactive source, in Point position, KeyModifiers modifiers = default)
         {
         {
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)target, position,
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), modifiers));
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), modifiers));
         }
         }
 
 
-        public void Up(IInteractive target, MouseButton mouseButton = MouseButton.Left, Point position = default,
+        public void Up(Interactive target, MouseButton mouseButton = MouseButton.Left, Point position = default,
             KeyModifiers modifiers = default)
             KeyModifiers modifiers = default)
             => Up(target, target, mouseButton, position, modifiers);
             => Up(target, target, mouseButton, position, modifiers);
         
         
-        public void Up(IInteractive target, IInteractive source, MouseButton mouseButton = MouseButton.Left,
+        public void Up(Interactive target, Interactive source, MouseButton mouseButton = MouseButton.Left,
             Point position = default, KeyModifiers modifiers = default)
             Point position = default, KeyModifiers modifiers = default)
         {
         {
             var conv = Convert(mouseButton);
             var conv = Convert(mouseButton);
@@ -95,23 +95,23 @@ namespace Avalonia.UnitTests
                 Move(target, source, position);
                 Move(target, source, position);
         }
         }
 
 
-        public void Click(IInteractive target, MouseButton button = MouseButton.Left, Point position = default,
+        public void Click(Interactive target, MouseButton button = MouseButton.Left, Point position = default,
             KeyModifiers modifiers = default)
             KeyModifiers modifiers = default)
             => Click(target, target, button, position, modifiers);
             => Click(target, target, button, position, modifiers);
-        public void Click(IInteractive target, IInteractive source, MouseButton button = MouseButton.Left, 
+        public void Click(Interactive target, Interactive source, MouseButton button = MouseButton.Left, 
             Point position = default, KeyModifiers modifiers = default)
             Point position = default, KeyModifiers modifiers = default)
         {
         {
             Down(target, source, button, position, modifiers);
             Down(target, source, button, position, modifiers);
             Up(target, source, button, position, modifiers);
             Up(target, source, button, position, modifiers);
         }
         }
         
         
-        public void Enter(IInteractive target)
+        public void Enter(Interactive target)
         {
         {
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerEnteredEvent, target, _pointer, (Visual)target, default,
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerEnteredEvent, target, _pointer, (Visual)target, default,
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));
         }
         }
 
 
-        public void Leave(IInteractive target)
+        public void Leave(Interactive target)
         {
         {
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerExitedEvent, target, _pointer, (Visual)target, default,
             target.RaiseEvent(new PointerEventArgs(InputElement.PointerExitedEvent, target, _pointer, (Visual)target, default,
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));