using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.Native.Interop; using Avalonia.VisualTree; namespace Avalonia.Native { class AvaloniaNativeDragSource : IPlatformDragSource { private readonly IAvaloniaNativeFactory _factory; public AvaloniaNativeDragSource(IAvaloniaNativeFactory factory) { _factory = factory; } private static TopLevel FindRoot(object? element) { while (element is Interactive interactive && element is not Visual) element = interactive.GetInteractiveParent(); if (element == null) return null; var visual = (Visual)element; return visual.GetVisualRoot() as TopLevel; } class DndCallback : NativeCallbackBase, IAvnDndResultCallback { private TaskCompletionSource _tcs; public DndCallback(TaskCompletionSource tcs) { _tcs = tcs; } public void OnDragAndDropComplete(AvnDragDropEffects effect) { _tcs?.TrySetResult((DragDropEffects)effect); _tcs = null; } } public Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects) { // Sanity check var tl = FindRoot(triggerEvent.Source); var view = tl?.PlatformImpl as WindowBaseImpl; if (view == null) throw new ArgumentException(); triggerEvent.Pointer.Capture(null); var tcs = new TaskCompletionSource(); var clipboardImpl = _factory.CreateDndClipboard(); using (var clipboard = new ClipboardImpl(clipboardImpl)) using (var cb = new DndCallback(tcs)) { if (data.Contains(DataFormats.Text)) // API is synchronous, so it's OK clipboard.SetTextAsync(data.GetText()).Wait(); view.BeginDraggingSession((AvnDragDropEffects)allowedEffects, triggerEvent.GetPosition(tl).ToAvnPoint(), clipboardImpl, cb, GCHandle.ToIntPtr(GCHandle.Alloc(data))); } return tcs.Task; } } }