using Avalonia.Interactivity; using Avalonia.VisualTree; using System.Linq; using Avalonia.Input.Raw; namespace Avalonia.Input { public class DragDropDevice : IDragDropDevice { public static readonly DragDropDevice Instance = new DragDropDevice(); private Interactive? _lastTarget = null; private Interactive? GetTarget(IInputRoot root, Point local) { var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); if (target != null && DragDrop.GetAllowDrop(target)) return target; return null; } private DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers) { if (target == null) return DragDropEffects.None; var p = inputRoot.TranslatePoint(point, target); if (!p.HasValue) return DragDropEffects.None; var args = new DragEventArgs(routedEvent, data, target, p.Value, modifiers) { RoutedEvent = routedEvent, DragEffects = operation }; target.RaiseEvent(args); return args.DragEffects; } private DragDropEffects DragEnter(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { _lastTarget = GetTarget(inputRoot, point); return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } private DragDropEffects DragOver(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { var target = GetTarget(inputRoot, point); if (target == _lastTarget) return RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data, modifiers); try { if (_lastTarget != null) _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } finally { _lastTarget = target; } } private void DragLeave(IInputElement inputRoot) { if (_lastTarget == null) return; try { _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); } finally { _lastTarget = null; } } private DragDropEffects Drop(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { try { return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data, modifiers); } finally { _lastTarget = null; } } public void ProcessRawEvent(RawInputEventArgs e) { if (!e.Handled && e is RawDragEvent margs) ProcessRawEvent(margs); } private void ProcessRawEvent(RawDragEvent e) { switch (e.Type) { case RawDragEventType.DragEnter: e.Effects = DragEnter(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers); break; case RawDragEventType.DragOver: e.Effects = DragOver(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers); break; case RawDragEventType.DragLeave: DragLeave(e.Root); break; case RawDragEventType.Drop: e.Effects = Drop(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers); break; } } } }