소스 검색

raise routed events on drag and drop

Florian Sundermann 7 년 전
부모
커밋
9ae9f6d221

+ 3 - 1
src/Avalonia.Controls/Application.cs

@@ -12,6 +12,7 @@ using Avalonia.Rendering;
 using Avalonia.Styling;
 using Avalonia.Threading;
 using System.Reactive.Concurrency;
+using Avalonia.Controls.DragDrop;
 
 namespace Avalonia
 {
@@ -234,7 +235,8 @@ namespace Avalonia
                 .Bind<IStyler>().ToConstant(_styler)
                 .Bind<ILayoutManager>().ToSingleton<LayoutManager>()
                 .Bind<IApplicationLifecycle>().ToConstant(this)
-                .Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance);
+                .Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
+                .Bind<IDragDispatcher>().ToConstant(DefaultDragDispatcher.Instance);
         }
     }
 }

+ 90 - 0
src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs

@@ -0,0 +1,90 @@
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.VisualTree;
+using System.Linq;
+
+namespace Avalonia.Controls.DragDrop
+{
+    class DefaultDragDispatcher : IDragDispatcher
+    {
+        public static readonly DefaultDragDispatcher Instance = new DefaultDragDispatcher();
+
+        private Interactive _lastTarget = null;
+        
+        private DefaultDragDispatcher()
+        {   
+        }
+
+        private Interactive GetTarget(IInputElement root, Point local)
+        {
+            var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
+            if (target != null && DragDrop.GetAcceptDrag(target))
+                return target;
+            return null;
+        }
+        
+        private DragOperation RaiseDragEvent(Interactive target, RoutedEvent<DragEventArgs> routedEvent, DragOperation operation, IDragData data)
+        {
+            if (target == null)
+                return DragOperation.None;
+            var args = new DragEventArgs(routedEvent, data)
+            {
+                RoutedEvent = routedEvent,
+                DragOperation = operation
+            };
+            target.RaiseEvent(args);
+            return args.DragOperation;
+        }
+        
+        public DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation)
+        {
+            _lastTarget = GetTarget(inputRoot, point);
+            return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, operation, data);
+        }
+
+        public DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation)
+        {
+            var target = GetTarget(inputRoot, point);
+
+            if (target == _lastTarget)
+                return RaiseDragEvent(target, DragDrop.DragOverEvent, operation, data);
+            
+            try
+            {
+                if (_lastTarget != null)
+                    _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+                return RaiseDragEvent(target, DragDrop.DragEnterEvent, operation, data);
+            }
+            finally
+            {
+                _lastTarget = target;
+            }            
+        }
+
+        public void DragLeave(IInputElement inputRoot)
+        {
+            if (_lastTarget == null)
+                return;
+            try
+            {
+                _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+            }
+            finally 
+            {
+                _lastTarget = null;
+            }
+        }
+
+        public DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation)
+        {
+            try
+            {
+                return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, operation, data);
+            }
+            finally 
+            {
+                _lastTarget = null;
+            }
+        }
+    }
+}

+ 24 - 0
src/Avalonia.Controls/DragDrop/DragDrop.cs

@@ -0,0 +1,24 @@
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls.DragDrop
+{
+    public sealed class DragDrop : AvaloniaObject
+    {
+        public static RoutedEvent<DragEventArgs> DragEnterEvent = RoutedEvent.Register<DragEventArgs>("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop));
+        public static RoutedEvent<RoutedEventArgs> DragLeaveEvent = RoutedEvent.Register<RoutedEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
+        public static RoutedEvent<DragEventArgs> DragOverEvent = RoutedEvent.Register<DragEventArgs>("DragOver", RoutingStrategies.Bubble, typeof(DragDrop));
+        public static RoutedEvent<DragEventArgs> DropEvent = RoutedEvent.Register<DragEventArgs>("Drop", RoutingStrategies.Bubble, typeof(DragDrop));
+
+        public static AvaloniaProperty<bool> AcceptDragProperty = AvaloniaProperty.RegisterAttached<Interactive, bool>("AcceptDrag", typeof(DragDrop), inherits: true);
+
+        public static bool GetAcceptDrag(Interactive interactive)
+        {
+            return interactive.GetValue(AcceptDragProperty);
+        }
+        
+        public static void SetAcceptDrag(Interactive interactive, bool value)
+        {
+            interactive.SetValue(AcceptDragProperty, value);
+        }
+    }
+}

+ 18 - 0
src/Avalonia.Controls/DragDrop/DragEventArgs.cs

@@ -0,0 +1,18 @@
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls.DragDrop
+{
+    public class DragEventArgs : RoutedEventArgs
+    {
+        public DragOperation DragOperation { get; set; }
+
+        public IDragData Data { get; private set; }
+
+        public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDragData data)
+            : base(routedEvent)
+        {
+            this.Data = data;
+        }
+
+    }
+}

+ 7 - 4
src/Avalonia.Controls/DragDrop/IDragDispatcher.cs

@@ -2,11 +2,14 @@
 
 namespace Avalonia.Controls.DragDrop
 {
+    /// <summary>
+    /// Dispatches Drag+Drop events to the correct visual targets, based on the input root and the drag location.
+    /// </summary>
     public interface IDragDispatcher
     {
-        DragOperation DragEnter(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation);
-        DragOperation DragOver(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation);
-        void DragLeave(IInputRoot inputRoot);
-        DragOperation Drop(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation);
+        DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation);
+        DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation);
+        void DragLeave(IInputElement inputRoot);
+        DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation);
     }
 }

+ 1 - 0
src/Avalonia.Controls/Properties/AssemblyInfo.cs

@@ -11,6 +11,7 @@ using Avalonia.Metadata;
 
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.DragDrop")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Embedding")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Presenters")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")]

+ 2 - 2
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@@ -201,7 +201,7 @@ namespace Avalonia.MonoMac
                 
                 dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp);
                 
-                return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None;
+                return dragOp != DragOperation.None;
             }
 
             public override bool PerformDragOperation(NSDraggingInfo sender)
@@ -216,7 +216,7 @@ namespace Avalonia.MonoMac
                 
                 dragOp = _dragDispatcher.Drop(root, pt, info, dragOp);
                 
-                return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None;
+                return dragOp != DragOperation.None;
             }