فهرست منبع

Removed IInteractive.

Steven Kirk 3 سال پیش
والد
کامیت
8347093f90
24فایلهای تغییر یافته به همراه124 افزوده شده و 150 حذف شده
  1. 15 12
      src/Avalonia.Base/Input/Gestures.cs
  2. 28 4
      src/Avalonia.Base/Input/IInputElement.cs
  3. 2 2
      src/Avalonia.Base/Input/KeyboardDevice.cs
  4. 1 1
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  5. 6 6
      src/Avalonia.Base/Input/PointerEventArgs.cs
  6. 1 1
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  7. 6 6
      src/Avalonia.Base/Interactivity/EventRoute.cs
  8. 0 53
      src/Avalonia.Base/Interactivity/IInteractive.cs
  9. 20 20
      src/Avalonia.Base/Interactivity/Interactive.cs
  10. 5 3
      src/Avalonia.Base/Interactivity/InteractiveExtensions.cs
  11. 1 1
      src/Avalonia.Base/Interactivity/RoutedEvent.cs
  12. 2 2
      src/Avalonia.Base/Interactivity/RoutedEventArgs.cs
  13. 3 4
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  14. 2 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  15. 2 2
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  16. 2 2
      src/Avalonia.Controls/SizeChangedEventArgs.cs
  17. 1 1
      src/Avalonia.Controls/TextChangedEventArgs.cs
  18. 1 1
      src/Avalonia.Controls/TextChangingEventArgs.cs
  19. 2 2
      src/Avalonia.Controls/TreeView.cs
  20. 6 7
      src/Avalonia.Native/AvaloniaNativeDragSource.cs
  21. 1 1
      tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs
  22. 1 1
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  23. 6 6
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
  24. 10 10
      tests/Avalonia.UnitTests/MouseTestHelper.cs

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

@@ -43,7 +43,7 @@ namespace Avalonia.Input
             RoutedEvent.Register<PointerDeltaEventArgs>(
                 "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;
 
         static Gestures()
@@ -52,32 +52,32 @@ namespace Avalonia.Input
             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);
         }
 
-        public static void AddDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> 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);
         }
 
-        public static void RemoveTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> 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);
         }
 
-        public static void RemoveRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.RemoveHandler(RightTappedEvent, handler);
         }
@@ -102,10 +102,12 @@ namespace Avalonia.Input
                 }
                 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;
-                        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) && 
                     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 settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
@@ -131,13 +134,13 @@ namespace Avalonia.Input
                     {
                         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.
                         //This behaviour matches UWP behaviour.
                         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 Avalonia.Interactivity;
 using Avalonia.Metadata;
-using Avalonia.VisualTree;
-
-#nullable enable
 
 namespace Avalonia.Input
 {
@@ -12,7 +9,7 @@ namespace Avalonia.Input
     /// Defines input-related functionality for a control.
     /// </summary>
     [NotClientImplementable]
-    public interface IInputElement : IInteractive
+    public interface IInputElement
     {
         /// <summary>
         /// Occurs when the control receives focus.
@@ -128,5 +125,32 @@ namespace Avalonia.Input
         /// Gets the key bindings for the element.
         /// </summary>
         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)
             {
-                var interactive = FocusedElement as IInteractive;
+                var interactive = FocusedElement as Interactive;
 
                 if (FocusedElement != null && 
                     (!((Visual)FocusedElement).IsAttachedToVisualTree ||
@@ -152,7 +152,7 @@ namespace Avalonia.Input
                     RoutedEvent = InputElement.LostFocusEvent,
                 });
 
-                interactive = element as IInteractive;
+                interactive = element as Interactive;
 
                 interactive?.RaiseEvent(new GotFocusEventArgs
                 {

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

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

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

@@ -7,7 +7,7 @@ namespace Avalonia.Input
     {
         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,
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
             : 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.
         /// </param>
         public void Add(
-            IInteractive target,
+            Interactive target,
             Delegate handler,
             RoutingStrategies routes,
             bool handledEventsToo = false,
@@ -60,7 +60,7 @@ namespace Avalonia.Interactivity
         /// Adds a class handler to the route.
         /// </summary>
         /// <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));
 
@@ -73,7 +73,7 @@ namespace Avalonia.Interactivity
         /// </summary>
         /// <param name="source">The event source.</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));
             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));
             }
 
-            IInteractive? lastTarget = null;
+            Interactive? lastTarget = null;
             var start = 0;
             var end = _route.Count;
             var step = 1;
@@ -177,7 +177,7 @@ namespace Avalonia.Interactivity
         private readonly struct RouteItem
         {
             public RouteItem(
-                IInteractive target,
+                Interactive target,
                 Delegate? handler,
                 Action<Delegate, object, RoutedEventArgs>? adapter,
                 RoutingStrategies routes,
@@ -190,7 +190,7 @@ namespace Avalonia.Interactivity
                 HandledEventsToo = handledEventsToo;
             }
 
-            public IInteractive Target { get; }
+            public Interactive Target { get; }
             public Delegate? Handler { get; }
             public Action<Delegate, object, RoutedEventArgs>? Adapter { 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>
     /// Base class for objects that raise routed events.
     /// </summary>
-    public class Interactive : Layoutable, IInteractive
+    public class Interactive : Layoutable
     {
         private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
 
         /// <summary>
         /// Gets the interactive parent of the object for bubbling and tunneling events.
         /// </summary>
-        IInteractive? IInteractive.InteractiveParent => this.VisualParent as IInteractive;
+        protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
 
         /// <summary>
         /// Adds a handler for the specified routed event.
@@ -125,21 +125,6 @@ namespace Avalonia.Interactivity
             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>
         /// Builds an event route for a routed event.
         /// </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
         /// 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
-        /// <see cref="EventRoute.RaiseEvent(IInteractive, RoutedEventArgs)"/> to raise the event.
+        /// <see cref="EventRoute.RaiseEvent(Interactive, RoutedEventArgs)"/> to raise the event.
         /// </remarks>
         protected EventRoute BuildEventRoute(RoutedEvent e)
         {
@@ -163,7 +148,7 @@ namespace Avalonia.Interactivity
             if (e.RoutingStrategies.HasAllFlags(RoutingStrategies.Bubble) ||
                 e.RoutingStrategies.HasAllFlags(RoutingStrategies.Tunnel))
             {
-                IInteractive? element = this;
+                Interactive? element = this;
 
                 while (element != null)
                 {
@@ -183,7 +168,7 @@ namespace Avalonia.Interactivity
                     result.AddClassHandler(this);
                 }
 
-                ((IInteractive)this).AddToEventRoute(e, result);
+                AddToEventRoute(e, result);
             }
 
             return result;
@@ -202,6 +187,21 @@ namespace Avalonia.Interactivity
             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
         {
             public EventSubscription(

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

@@ -5,7 +5,7 @@ using System.Reactive.Linq;
 namespace Avalonia.Interactivity
 {
     /// <summary>
-    /// Provides extension methods for the <see cref="IInteractive"/> interface.
+    /// Provides extension methods for the <see cref="Interactive"/> interface.
     /// </summary>
     public static class InteractiveExtensions
     {
@@ -19,7 +19,7 @@ namespace Avalonia.Interactivity
         /// <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>
-        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,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
@@ -31,6 +31,8 @@ namespace Avalonia.Interactivity
                 state => state.instance.RemoveHandler(state.routedEvent, state.handler));
         }
 
+        public static Interactive? GetInteractiveParent(this Interactive o) => o.InteractiveParent;
+
         /// <summary>
         /// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
         /// </summary>
@@ -42,7 +44,7 @@ namespace Avalonia.Interactivity
         /// An observable which fires each time the event is raised.
         /// </returns>
         public static IObservable<TEventArgs> GetObservable<TEventArgs>(
-            this IInteractive o,
+            this Interactive o,
             RoutedEvent<TEventArgs> routedEvent,
             RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
             bool handledEventsToo = false)

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

@@ -116,7 +116,7 @@ namespace Avalonia.Interactivity
         public IDisposable AddClassHandler<TTarget>(
             Action<TTarget, TEventArgs> handler,
             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)
             {

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

@@ -28,7 +28,7 @@ namespace Avalonia.Interactivity
         /// </summary>
         /// <param name="routedEvent">The routed event associated with these event args.</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;
             Source = source;
@@ -55,6 +55,6 @@ namespace Avalonia.Interactivity
         /// <summary>
         /// Gets or sets the source object that raised the routed event.
         /// </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
 {
-    public class OverlayPopupHost : ContentControl, IPopupHost, IInteractive, IManagedPopupPositionerPopup
+    public class OverlayPopupHost : ContentControl, IPopupHost, IManagedPopupPositionerPopup
     {
         /// <summary>
         /// Defines the <see cref="Transform"/> property.
@@ -42,15 +42,14 @@ namespace Avalonia.Controls.Primitives
             set => SetValue(TransformProperty, value);
         }
 
-        /// <inheritdoc/>
-        IInteractive? IInteractive.InteractiveParent => Parent;
-
         bool IPopupHost.Topmost
         {
             get => false;
             set { /* Not currently supported in overlay popups */ }
         }
 
+        protected internal override Interactive? InteractiveParent => Parent;
+
         public void Dispose() => Hide();
 
 

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

@@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives
     /// <summary>
     /// The root window of a <see cref="Popup"/>.
     /// </summary>
-    public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
+    public sealed class PopupRoot : WindowBase, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
     {
         /// <summary>
         /// Defines the <see cref="Transform"/> property.
@@ -72,7 +72,7 @@ namespace Avalonia.Controls.Primitives
         /// <remarks>
         /// Popup events are passed to their parent window. This facilitates this.
         /// </remarks>
-        IInteractive? IInteractive.InteractiveParent => Parent;
+        protected internal override Interactive? InteractiveParent => Parent;
 
         /// <summary>
         /// 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>
         /// <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>
-        protected Control? GetContainerFromEventSource(IInteractive? eventSource)
+        protected Control? GetContainerFromEventSource(object? eventSource)
         {
             for (var current = eventSource as Visual; current != null; current = current.VisualParent)
             {
@@ -780,7 +780,7 @@ namespace Avalonia.Controls.Primitives
         /// false.
         /// </returns>
         protected bool UpdateSelectionFromEventSource(
-            IInteractive? eventSource,
+            object? eventSource,
             bool select = true,
             bool rangeModifier = false,
             bool toggleModifier = false,

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

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

+ 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)
         {
         }

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

@@ -752,7 +752,7 @@ namespace Avalonia.Controls
         /// false.
         /// </returns>
         protected bool UpdateSelectionFromEventSource(
-            IInteractive eventSource,
+            object eventSource,
             bool select = true,
             bool rangeModifier = false,
             bool toggleModifier = false,
@@ -774,7 +774,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <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>
-        protected TreeViewItem? GetContainerFromEventSource(IInteractive eventSource)
+        protected TreeViewItem? GetContainerFromEventSource(object eventSource)
         {
             var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
                 .OfType<TreeViewItem>()

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

@@ -6,7 +6,6 @@ using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Interactivity;
 using Avalonia.Native.Interop;
-using Avalonia.Platform;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Native
@@ -19,14 +18,14 @@ namespace Avalonia.Native
         {
             _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;
-            var visual = (Visual)interactive;
+            var visual = (Visual)element;
             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>();
             EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s).Name);
             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) =>
             {

+ 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 };
         }

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

@@ -11,15 +11,15 @@ namespace Avalonia.Controls.UnitTests.Platform
 {
     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);
 
-        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);
-
-        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),
             default, MouseButton.Left);
 

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

@@ -38,13 +38,13 @@ namespace Avalonia.UnitTests
 
         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)
         {
             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)
         {
             _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,
                 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)
             => 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)
         {
             var conv = Convert(mouseButton);
@@ -95,23 +95,23 @@ namespace Avalonia.UnitTests
                 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)
             => 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)
         {
             Down(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,
                 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,
                 Timestamp(), new PointerPointProperties((RawInputModifiers)_pressedButtons, PointerUpdateKind.Other), KeyModifiers.None));