From 9ae9f6d2218de3bbde789fb044f22b773b31177d Mon Sep 17 00:00:00 2001 From: Florian Sundermann Date: Sat, 3 Mar 2018 12:40:06 +0100 Subject: [PATCH] raise routed events on drag and drop --- src/Avalonia.Controls/Application.cs | 4 +- .../DragDrop/DefaultDragDispatcher.cs | 90 +++++++++++++++++++ src/Avalonia.Controls/DragDrop/DragDrop.cs | 24 +++++ .../DragDrop/DragEventArgs.cs | 18 ++++ .../DragDrop/IDragDispatcher.cs | 11 ++- .../Properties/AssemblyInfo.cs | 1 + src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 4 +- 7 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs create mode 100644 src/Avalonia.Controls/DragDrop/DragDrop.cs create mode 100644 src/Avalonia.Controls/DragDrop/DragEventArgs.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 06c1a8b4cc..63b17530ff 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/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().ToConstant(_styler) .Bind().ToSingleton() .Bind().ToConstant(this) - .Bind().ToConstant(AvaloniaScheduler.Instance); + .Bind().ToConstant(AvaloniaScheduler.Instance) + .Bind().ToConstant(DefaultDragDispatcher.Instance); } } } diff --git a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs new file mode 100644 index 0000000000..2ce5a32dc5 --- /dev/null +++ b/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()?.FirstOrDefault(); + if (target != null && DragDrop.GetAcceptDrag(target)) + return target; + return null; + } + + private DragOperation RaiseDragEvent(Interactive target, RoutedEvent 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; + } + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Controls/DragDrop/DragDrop.cs new file mode 100644 index 0000000000..3645409a5a --- /dev/null +++ b/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 DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DragOverEvent = RoutedEvent.Register("DragOver", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DropEvent = RoutedEvent.Register("Drop", RoutingStrategies.Bubble, typeof(DragDrop)); + + public static AvaloniaProperty AcceptDragProperty = AvaloniaProperty.RegisterAttached("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); + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs new file mode 100644 index 0000000000..e8bd2528c7 --- /dev/null +++ b/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 routedEvent, IDragData data) + : base(routedEvent) + { + this.Data = data; + } + + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs index 5c39635dd0..05a211ff76 100644 --- a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs @@ -2,11 +2,14 @@ namespace Avalonia.Controls.DragDrop { + /// + /// Dispatches Drag+Drop events to the correct visual targets, based on the input root and the drag location. + /// 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); } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index ae8c88f7e8..b0877e0fb7 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/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")] diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 91e822f802..711f6d2787 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/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; }