Skip to content

Commit

Permalink
feat(dragdrop): Initial support for drop from external macOS apps
Browse files Browse the repository at this point in the history
  • Loading branch information
robloo committed Oct 19, 2020
1 parent 1f8b6e5 commit f594af3
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 29 deletions.
12 changes: 6 additions & 6 deletions src/Uno.UI/Controls/Window.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public Window(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore buffering
/// </remarks>
/// <param name="info">Information about the dragging session from the sender.</param>
/// <returns>The accepted drag operation(s).</returns>
[Export("draggingEntered:")]
[Export("draggingEntered:")] // Do not remove
public virtual NSDragOperation DraggingEntered(NSDraggingInfo draggingInfo)
{
try
Expand Down Expand Up @@ -131,7 +131,7 @@ public virtual NSDragOperation DraggingEntered(NSDraggingInfo draggingInfo)
/// See remarks in <see cref="DraggingEntered(NSDraggingInfo)"/>
/// </remarks>
/// <param name="draggingInfo">Information about the dragging session from the sender.</param>
[Export("draggingUpdated:")]
[Export("draggingUpdated:")] // Do not remove
public virtual NSDragOperation DraggingUpdated(NSDraggingInfo draggingInfo)
{
try
Expand Down Expand Up @@ -161,7 +161,7 @@ public virtual NSDragOperation DraggingUpdated(NSDraggingInfo draggingInfo)
/// See remarks in <see cref="DraggingEntered(NSDraggingInfo)"/>
/// </remarks>
/// <param name="draggingInfo">Information about the dragging session from the sender.</param>
[Export("draggingEnded:")]
[Export("draggingEnded:")] // Do not remove
public virtual void DraggingEnded(NSDraggingInfo draggingInfo)
{
// Not used in this context.
Expand All @@ -177,7 +177,7 @@ public virtual void DraggingEnded(NSDraggingInfo draggingInfo)
/// See remarks in <see cref="DraggingEntered(NSDraggingInfo)"/>
/// </remarks>
/// <param name="draggingInfo">Information about the dragging session from the sender.</param>
[Export("draggingExited:")]
[Export("draggingExited:")] // Do not remove
public virtual void DraggingExited(NSDraggingInfo draggingInfo)
{
return;
Expand All @@ -199,7 +199,7 @@ public virtual void DraggingExited(NSDraggingInfo draggingInfo)
/// </summary>
/// <param name="draggingInfo">Information about the dragging session from the sender.</param>
/// <returns>True if the destination accepts the drag operation; otherwise, false. </returns>
[Export("prepareForDragOperation:")]
[Export("prepareForDragOperation:")] // Do not remove
public virtual bool PrepareForDragOperation(AppKit.NSDraggingInfo draggingInfo)
{
// Always return true as UWP doesn't really have an equivalent step.
Expand All @@ -212,7 +212,7 @@ public virtual bool PrepareForDragOperation(AppKit.NSDraggingInfo draggingInfo)
/// </summary>
/// <param name="draggingInfo">Information about the dragging session from the sender.</param>
/// <returns>True if the destination accepts the data; otherwise, false.</returns>
[Export("performDragOperation:")]
[Export("performDragOperation:")] // Do not remove
public virtual bool PerformDragOperation(NSDraggingInfo draggingInfo)
{
try
Expand Down
95 changes: 81 additions & 14 deletions src/Uno.UI/UI/Xaml/DragDropExtension.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@
using Uno.Logging;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Point = Windows.Foundation.Point;
using UIElement = Windows.UI.Xaml.UIElement;

namespace Windows.ApplicationModel.DataTransfer.DragDrop.Core
{
internal class MacOSDragDropExtension : IDragDropExtension
{
private readonly long _fakePointerId = Pointer.CreateUniqueIdForUnknownPointer();
private readonly DragDropManager _manager;
private NSWindow _nativeWindowHost;
private Uno.UI.Controls.Window _window;

public MacOSDragDropExtension(DragDropManager owner)
{
_manager = (DragDropManager)owner;

_nativeWindowHost = AppKit.NSApplication.SharedApplication.DangerousWindows[0];
_window = (Uno.UI.Controls.Window)CoreWindow.GetForCurrentThread()!._window;

_window.DraggingEnteredAction = OnDraggingEnteredEvent;
Expand All @@ -37,22 +36,36 @@ public MacOSDragDropExtension(DragDropManager owner)

private NSDragOperation OnDraggingEnteredEvent(NSDraggingInfo draggingInfo)
{
var source = new DragEventSource(_fakePointerId, draggingInfo, _window);
var data = DataPackage.CreateFromNativeDragDropData(draggingInfo);
var allowedOperations = DataPackageOperation.Copy; // Should match with return value
var info = new CoreDragInfo(source, data, allowedOperations);

CoreDragDropManager.GetForCurrentView()?.DragStarted(info);

// Note: No need to _manager.ProcessMove, the DragStarted will actually have the same effect

// To allow the macOS drag to continue, we must assume drop is supported here.
// This will immediately be updated within the OnDraggingUpdated method.
return NSDragOperation.Copy;
}

private NSDragOperation OnDraggingUpdated(NSDraggingInfo draggingInfo)
{
return NSDragOperation.Copy;
var operation = _manager.ProcessMoved(new DragEventSource(_fakePointerId, draggingInfo, _window));
return ToNSDragOperation(operation);
}

private void OnDraggingExited(NSDraggingInfo draggingInfo)
{
_ = _manager.ProcessAborted(new DragEventSource(_fakePointerId, draggingInfo, _window));
return;
}

private bool OnPerformDragOperation(NSDraggingInfo draggingInfo)
{
return true;
var operation = _manager.ProcessDropped(new DragEventSource(_fakePointerId, draggingInfo, _window));
return (operation != DataPackageOperation.None);
}

public void StartNativeDrag(CoreDragInfo info)
Expand All @@ -63,26 +76,57 @@ public void StartNativeDrag(CoreDragInfo info)
NSDraggingSource source = new NSDraggingSource();
NSEvent sourceEvent = new NSEvent();
var text = await info.Data.GetTextAsync();
var draggingItem = new NSDraggingItem((NSString)text);
draggingItem.DraggingFrame = new CoreGraphics.CGRect(0, 0, 1, 1);
//_nativeView?.BeginDraggingSession(new[] { draggingItem }, sourceEvent, source);
_window.ContentView.BeginDraggingSession(
await DataPackage.CreateNativeDragDropData(info.Data),
sourceEvent,
source);
}
catch (Exception e)
{
this.Log().Error("Failed to start native Drag and Drop.", e);
}
});

/// <summary>
/// Converts the given UWP <see cref="DataPackageOperation"/> into the equivalent macOS
/// <see cref="NSDragOperation"/>.
/// </summary>
/// <param name="uwpOperation">The UWP <see cref="DataPackageOperation"/> to convert.</param>
/// <returns>The equivalent macOS <see cref="NSDragOperation"/>; otherwise, <see cref="NSDragOperation.None"/>.</returns>
private static NSDragOperation ToNSDragOperation(DataPackageOperation uwpOperation)
{
NSDragOperation result = NSDragOperation.None;

if (uwpOperation.HasFlag(DataPackageOperation.Copy))
{
result |= NSDragOperation.Copy;
}

if (uwpOperation.HasFlag(DataPackageOperation.Link))
{
result |= NSDragOperation.Link;
}

if (uwpOperation.HasFlag(DataPackageOperation.Move))
{
result |= NSDragOperation.Move;
}

return result;
}

private class DragEventSource : IDragEventSource
{
private static long _nextFrameId;
private readonly NSDraggingInfo _macOSDraggingInfo;
private readonly NSWindow _window;

public DragEventSource(long pointerId)
public DragEventSource(long pointerId, NSDraggingInfo draggingInfo, NSWindow window)
{
Id = pointerId;

_macOSDraggingInfo = draggingInfo;
_window = window;
}

public long Id { get; }
Expand All @@ -92,13 +136,36 @@ public DragEventSource(long pointerId)
/// <inheritdoc />
public (Point location, DragDropModifiers modifier) GetState()
{
return (new Windows.Foundation.Point(0, 0), DragDropModifiers.None);
var windowLocation = _window.ContentView.ConvertPointFromView(_macOSDraggingInfo.DraggingLocation, null);
var location = new Windows.Foundation.Point(windowLocation.X, windowLocation.Y);

// macOS requires access to NSEvent.ModifierFlags to determine key press.
// This isn't available from the dragging info.
var mods = DragDropModifiers.None;

return (location, mods);
}

/// <inheritdoc />
public Point GetPosition(object? relativeTo)
{
return new Point(0, 0);
var windowLocation = _window.ContentView.ConvertPointFromView(_macOSDraggingInfo.DraggingLocation, null);
var rawPosition = new Point(windowLocation.X, windowLocation.Y);

if (relativeTo is null)
{
return rawPosition;
}

if (relativeTo is UIElement elt)
{
var eltToRoot = UIElement.GetTransform(elt, null);
Matrix3x2.Invert(eltToRoot, out var rootToElt);

return rootToElt.Transform(rawPosition);
}

throw new InvalidOperationException("The relative to must be a UIElement.");
}
}
}
Expand Down
52 changes: 43 additions & 9 deletions src/Uno.UWP/ApplicationModel/DataTransfer/DataPackage.macOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,56 @@ internal static DataPackageView GetFromNativeClipboard()
}

/// <summary>
/// Sets the contents of the <see cref="DataPackage"/> to the native drag and drop manager.
/// Creates new, native drag and drop data from the contents of the given <see cref="DataPackageView"/>.
/// </summary>
/// <param name="content">The contents to set to the native drag and drop manager.</param>
internal static void SetForNativeDragDrop(DataPackage content)
/// <param name="data">The content to create the native drag and drop data from.</param>
internal static async Task<NSDraggingItem[]> CreateNativeDragDropData(DataPackageView data)
{
return;
NSDraggingItem draggingItem;
var items = new List<NSDraggingItem>();

if (data?.Contains(StandardDataFormats.Html) ?? false)
{
var html = await data.GetHtmlFormatAsync();

if (!string.IsNullOrEmpty(html))
{

}
}

if (data?.Contains(StandardDataFormats.Rtf) ?? false)
{
var rtf = await data.GetRtfAsync();

if (!string.IsNullOrEmpty(rtf))
{

}
}

if (data?.Contains(StandardDataFormats.Text) ?? false)
{
var text = await data.GetTextAsync();

if (!string.IsNullOrEmpty(text))
{
draggingItem = new NSDraggingItem((NSString)text);
draggingItem.DraggingFrame = new CoreGraphics.CGRect(0, 0, 1, 1); // Must be set
items.Add(draggingItem);
}
}

return items.ToArray();
}

/// <summary>
/// Gets the contents of the native drag and drop manager.
/// Creates a new <see cref="DataPackageView"/> from the native drag and drop data.
/// </summary>
/// <returns>A new <see cref="DataPackageView"/> representing the native drag and drop manager contents.</returns>
internal static DataPackageView GetFromNativeDragDrop()
/// <returns>A new <see cref="DataPackageView"/> representing the native drag and drop data.</returns>
internal static DataPackageView CreateFromNativeDragDropData(NSDraggingInfo draggingInfo)
{
// More worked needed, signature may change
return (new DataPackage()).GetView();
return GetFromNative(draggingInfo.DraggingPasteboard);
}

private static void SetToNative(DataPackage content, NSPasteboard pasteboard)
Expand Down

0 comments on commit f594af3

Please sign in to comment.