diff --git a/src/Avalonia.Controls/DragDrop/DataFormats.cs b/src/Avalonia.Controls/DragDrop/DataFormats.cs
new file mode 100644
index 0000000000..f46651ed3b
--- /dev/null
+++ b/src/Avalonia.Controls/DragDrop/DataFormats.cs
@@ -0,0 +1,15 @@
+namespace Avalonia.Controls.DragDrop
+{
+ public static class DataFormats
+ {
+ ///
+ /// Dataformat for plaintext
+ ///
+ public static string Text = nameof(Text);
+
+ ///
+ /// Dataformat for one or more filenames
+ ///
+ public static string FileNames = nameof(FileNames);
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/DragDrop/DragOperation.cs b/src/Avalonia.Controls/DragDrop/DragOperation.cs
new file mode 100644
index 0000000000..ffc6f666f7
--- /dev/null
+++ b/src/Avalonia.Controls/DragDrop/DragOperation.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Avalonia.Controls.DragDrop
+{
+ [Flags]
+ public enum DragOperation
+ {
+ None = 0,
+ Copy = 1,
+ Move = 2,
+ Link = 4,
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/DragDrop/IDragData.cs b/src/Avalonia.Controls/DragDrop/IDragData.cs
new file mode 100644
index 0000000000..b6dc53d32d
--- /dev/null
+++ b/src/Avalonia.Controls/DragDrop/IDragData.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace Avalonia.Controls.DragDrop
+{
+ public interface IDragData
+ {
+ IEnumerable GetDataFormats();
+
+ bool Contains(string dataFormat);
+
+ string GetText();
+
+ IEnumerable GetFileNames();
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs
new file mode 100644
index 0000000000..5c39635dd0
--- /dev/null
+++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs
@@ -0,0 +1,12 @@
+using Avalonia.Input;
+
+namespace Avalonia.Controls.DragDrop
+{
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj
index 5f6be91571..c31c131ea9 100644
--- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj
+++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
True
@@ -19,4 +19,4 @@
-
+
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs
new file mode 100644
index 0000000000..3abcb8c68c
--- /dev/null
+++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using Avalonia.Controls.DragDrop;
+using MonoMac.AppKit;
+
+namespace Avalonia.MonoMac
+{
+ class DraggingInfo : IDragData
+ {
+ private readonly NSDraggingInfo _info;
+
+ public DraggingInfo(NSDraggingInfo info)
+ {
+ _info = info;
+ }
+
+
+ internal static NSDragOperation ConvertDragOperation(DragOperation d)
+ {
+ NSDragOperation result = NSDragOperation.None;
+ if (d.HasFlag(DragOperation.Copy))
+ result |= NSDragOperation.Copy;
+ if (d.HasFlag(DragOperation.Link))
+ result |= NSDragOperation.Link;
+ if (d.HasFlag(DragOperation.Move))
+ result |= NSDragOperation.Move;
+ return result;
+ }
+
+ internal static DragOperation ConvertDragOperation(NSDragOperation d)
+ {
+ DragOperation result = DragOperation.None;
+ if (d.HasFlag(NSDragOperation.Copy))
+ result |= DragOperation.Copy;
+ if (d.HasFlag(NSDragOperation.Link))
+ result |= DragOperation.Link;
+ if (d.HasFlag(NSDragOperation.Move))
+ result |= DragOperation.Move;
+ return result;
+ }
+
+ public Point Location => new Point(_info.DraggingLocation.X, _info.DraggingLocation.Y);
+
+ public IEnumerable GetDataFormats()
+ {
+ yield break;
+ }
+
+ public string GetText()
+ {
+ return null;
+ }
+
+ public IEnumerable GetFileNames()
+ {
+ yield break;
+ }
+
+ public bool Contains(string dataFormat)
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
index 667ee12fa0..91e822f802 100644
--- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
+++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Avalonia.Controls.DragDrop;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
@@ -36,6 +37,7 @@ namespace Avalonia.MonoMac
bool _isLeftPressed, _isRightPressed, _isMiddlePressed;
private readonly IMouseDevice _mouse;
private readonly IKeyboardDevice _keyboard;
+ private readonly IDragDispatcher _dragDispatcher;
private NSTrackingArea _area;
private NSCursor _cursor;
private bool _nonUiRedrawQueued;
@@ -52,6 +54,11 @@ namespace Avalonia.MonoMac
_tl = tl;
_mouse = AvaloniaLocator.Current.GetService();
_keyboard = AvaloniaLocator.Current.GetService();
+ _dragDispatcher = AvaloniaLocator.Current.GetService();
+
+ RegisterForDraggedTypes(new string[] {
+ "public.data" // register for any kind of data.
+ });
}
protected override void Dispose(bool disposing)
@@ -144,6 +151,75 @@ namespace Avalonia.MonoMac
UpdateCursor();
}
+ public override NSDragOperation DraggingEntered(NSDraggingInfo sender)
+ {
+ IInputRoot root = _tl?.InputRoot;
+ if (root == null || _dragDispatcher == null)
+ return NSDragOperation.None;
+
+ var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask);
+ DraggingInfo info = new DraggingInfo(sender);
+ var pt = TranslateLocalPoint(info.Location);
+
+ dragOp = _dragDispatcher.DragEnter(root, pt, info, dragOp);
+
+ return DraggingInfo.ConvertDragOperation(dragOp);
+ }
+
+ public override NSDragOperation DraggingUpdated(NSDraggingInfo sender)
+ {
+ IInputRoot root = _tl?.InputRoot;
+ if (root == null || _dragDispatcher == null)
+ return NSDragOperation.None;
+
+ var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask);
+ DraggingInfo info = new DraggingInfo(sender);
+ var pt = TranslateLocalPoint(info.Location);
+
+ dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp);
+
+ return DraggingInfo.ConvertDragOperation(dragOp);
+ }
+
+ public override void DraggingExited(NSDraggingInfo sender)
+ {
+ IInputRoot root = _tl?.InputRoot;
+ if (root == null || _dragDispatcher == null)
+ return;
+ _dragDispatcher.DragLeave(root);
+ }
+
+ public override bool PrepareForDragOperation(NSDraggingInfo sender)
+ {
+ IInputRoot root = _tl?.InputRoot;
+ if (root == null || _dragDispatcher == null)
+ return false;
+
+ var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask);
+ DraggingInfo info = new DraggingInfo(sender);
+ var pt = TranslateLocalPoint(info.Location);
+
+ dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp);
+
+ return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None;
+ }
+
+ public override bool PerformDragOperation(NSDraggingInfo sender)
+ {
+ IInputRoot root = _tl?.InputRoot;
+ if (root == null || _dragDispatcher == null)
+ return false;
+
+ var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask);
+ DraggingInfo info = new DraggingInfo(sender);
+ var pt = TranslateLocalPoint(info.Location);
+
+ dragOp = _dragDispatcher.Drop(root, pt, info, dragOp);
+
+ return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None;
+ }
+
+
public override void SetFrameSize(CGSize newSize)
{
lock (SyncRoot)