internal static async Task <NSDraggingItem[]> CreateNativeDragDropData( DataPackageView data, Point startPoint) { NSDraggingItem draggingItem; var items = new List <NSDraggingItem>(); double maxFrameDimension = 300.0; // May be adjusted var defaultFrameRect = new CoreGraphics.CGRect(startPoint.X, startPoint.Y, 100, 30); /* Note that NSDraggingItems are required by the BeginDraggingSession methods. * Therefore, that is what is constructed here instead of pasteboard items. * * For several types such as NSString or NSImage, they implement the INSPasteboardWriting interface and * can therefore be used to directly construct an NSDraggingItem. * However, for other types (such as HTML) the full pasteboard item must be constructed first defining * both its type and string content. * * The dragging frame is used to represent the visual of the item being dragged. This could be a * preview of the image or sample text. At minimum, macOS requires the DraggingFrame property of the * NSDraggingItem to be set with a CGRect or the app will crash. It is however better to set both * the frame bounds and content at the same time with .SetDraggingFrame(). For caveats see: * https://developer.apple.com/documentation/appkit/nsdraggingitem/1528746-setdraggingframe * * Because Uno does not currently support the DragUI, this code only generates a real drag visual * for images where a visual is already defined. For other types such as text, no visual will be * generated. In the future, when DragUI and its corresponding image is supported, this can change. * */ if (data?.Contains(StandardDataFormats.Bitmap) ?? false) { NSImage?image = null; using (var stream = (await(await data.GetBitmapAsync()).OpenReadAsync()).AsStream()) { if (stream != null) { using (var ms = new MemoryStream()) { await stream.CopyToAsync(ms); ms.Flush(); ms.Position = 0; image = NSImage.FromStream(ms); } } } if (image != null) { draggingItem = new NSDraggingItem(image); // For an NSImage, we will use the image itself as the dragging visual. // The visual should be no larger than the max dimension setting and is therefore scaled. NSBitmapImageRep rep = new NSBitmapImageRep(image.CGImage); int width = (int)rep.PixelsWide; int height = (int)rep.PixelsHigh; double scale = maxFrameDimension / Math.Max(width, height); // Dragging frame must be set draggingItem.SetDraggingFrame( new CoreGraphics.CGRect(startPoint.X, startPoint.Y, width * scale, height * scale), image); items.Add(draggingItem); } } if (data?.Contains(StandardDataFormats.Html) ?? false) { var html = await data.GetHtmlFormatAsync(); if (!string.IsNullOrEmpty(html)) { var pasteboardItem = new NSPasteboardItem(); pasteboardItem.SetStringForType(html, NSPasteboard.NSPasteboardTypeHTML); draggingItem = new NSDraggingItem(pasteboardItem); draggingItem.DraggingFrame = defaultFrameRect; // Must be set items.Add(draggingItem); } } if (data?.Contains(StandardDataFormats.Rtf) ?? false) { var rtf = await data.GetRtfAsync(); if (!string.IsNullOrEmpty(rtf)) { // Use `NSPasteboardTypeRTF` instead of `NSPasteboardTypeRTFD` for max compatibility var pasteboardItem = new NSPasteboardItem(); pasteboardItem.SetStringForType(rtf, NSPasteboard.NSPasteboardTypeRTF); draggingItem = new NSDraggingItem(pasteboardItem); draggingItem.DraggingFrame = defaultFrameRect; // Must be set items.Add(draggingItem); } } if (data?.Contains(StandardDataFormats.StorageItems) ?? false) { var storageItems = await data.GetStorageItemsAsync(); if (storageItems.Count > 0) { // Not currently supported } } if (data?.Contains(StandardDataFormats.Text) ?? false) { var text = await data.GetTextAsync(); if (!string.IsNullOrEmpty(text)) { draggingItem = new NSDraggingItem((NSString)text); draggingItem.DraggingFrame = defaultFrameRect; // Must be set items.Add(draggingItem); } } if (data != null) { var uri = DataPackage.CombineUri( data.Contains(StandardDataFormats.WebLink) ? (await data.GetWebLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.ApplicationLink) ? (await data.GetApplicationLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.Uri) ? (await data.GetUriAsync()).ToString() : null); if (string.IsNullOrEmpty(uri) == false) { draggingItem = new NSDraggingItem(new NSUrl(uri)); draggingItem.DraggingFrame = defaultFrameRect; // Must be set items.Add(draggingItem); } } return(items.ToArray()); }
private static async Task SetToNativeAsync(DataPackage content, NSPasteboard pasteboard) { if (pasteboard is null) { throw new ArgumentException(nameof(pasteboard)); } var data = content?.GetView(); var declaredTypes = new List <string>(); string?uri = null; /* Note that order is somewhat important here. * * According to the docs: * "types should be ordered according to the preference of the source application, * with the most preferred type coming first" * https://developer.apple.com/documentation/appkit/nspasteboard/1533561-declaretypes?language=objc * * This means we want to process certain types like HTML/RTF before general plain text * as they are more specific. * * Types are also declared before setting */ // Declare types if (data?.Contains(StandardDataFormats.Html) ?? false) { declaredTypes.Add(NSPasteboard.NSPasteboardTypeHTML); } if (data?.Contains(StandardDataFormats.Rtf) ?? false) { // Use `NSPasteboardTypeRTF` instead of `NSPasteboardTypeRTFD` for max compatibility declaredTypes.Add(NSPasteboard.NSPasteboardTypeRTF); } if (data?.Contains(StandardDataFormats.Text) ?? false) { declaredTypes.Add(NSPasteboard.NSPasteboardTypeString); } if (data != null) { uri = DataPackage.CombineUri( data.Contains(StandardDataFormats.WebLink) ? (await data.GetWebLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.ApplicationLink) ? (await data.GetApplicationLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.Uri) ? (await data.GetUriAsync()).ToString() : null); if (string.IsNullOrEmpty(uri) == false) { declaredTypes.Add(NSPasteboard.NSPasteboardTypeUrl); } } pasteboard.DeclareTypes(declaredTypes.ToArray(), null); // Set content if (data?.Contains(StandardDataFormats.Html) ?? false) { var html = await data.GetHtmlFormatAsync(); pasteboard.SetStringForType(html ?? string.Empty, NSPasteboard.NSPasteboardTypeHTML); } if (data?.Contains(StandardDataFormats.Rtf) ?? false) { var rtf = await data.GetRtfAsync(); pasteboard.SetStringForType(rtf ?? string.Empty, NSPasteboard.NSPasteboardTypeRTF); } if (data?.Contains(StandardDataFormats.Text) ?? false) { var text = await data.GetTextAsync(); pasteboard.SetStringForType(text ?? string.Empty, NSPasteboard.NSPasteboardTypeString); } if (string.IsNullOrEmpty(uri) == false) { pasteboard.SetStringForType(uri !.ToString(), NSPasteboard.NSPasteboardTypeUrl); } return; }
internal static async Task SetContentAsync(DataPackage content) { var data = content?.GetView(); var items = new List <ClipData.Item>(); var mimeTypes = new List <string>(); if (data?.Contains(StandardDataFormats.Text) ?? false) { var text = await data.GetTextAsync(); items.Add(new ClipData.Item(text)); mimeTypes.Add("text/plaintext"); } if (data != null) { var uri = DataPackage.CombineUri( data.Contains(StandardDataFormats.WebLink) ? (await data.GetWebLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.ApplicationLink) ? (await data.GetApplicationLinkAsync()).ToString() : null, data.Contains(StandardDataFormats.Uri) ? (await data.GetUriAsync()).ToString() : null); if (string.IsNullOrEmpty(uri) == false) { var androidUri = Android.Net.Uri.Parse(uri); items.Add(new ClipData.Item(androidUri)); mimeTypes.Add("text/uri-list"); } } if (data?.Contains(StandardDataFormats.Html) ?? false) { var html = await data.GetHtmlFormatAsync(); // Matches all tags Regex regex = new Regex("(<.*?>\\s*)+", RegexOptions.Singleline); // Replace tags by spaces and trim var plainText = regex.Replace(html, " ").Trim(); items.Add(new ClipData.Item(plainText, html)); mimeTypes.Add("text/html"); } // Set all the data formats to the Android clipboard if (items.Count > 0) { ClipData clipData = new ClipData( new ClipDescription(ClipboardDataLabel, mimeTypes.ToArray()), items[0]); for (int itemIndex = 1; itemIndex < items.Count; itemIndex++) { clipData.AddItem(items[itemIndex]); } var manager = ContextHelper.Current.GetSystemService(Context.ClipboardService) as ClipboardManager; if (manager is null) { return; } manager.PrimaryClip = clipData; } else { // Clear clipboard Clear(); } }