DragSource.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Reactive.Linq;
  8. using System.Reactive.Subjects;
  9. using System.Runtime.InteropServices;
  10. using System.Runtime.Serialization.Formatters.Binary;
  11. using System.Threading.Tasks;
  12. using Avalonia.Controls;
  13. using Avalonia.Input;
  14. using Avalonia.Input.Platform;
  15. using Avalonia.Input.Raw;
  16. using MonoMac;
  17. using MonoMac.AppKit;
  18. using MonoMac.CoreGraphics;
  19. using MonoMac.Foundation;
  20. using MonoMac.OpenGL;
  21. namespace Avalonia.MonoMac
  22. {
  23. public class DragSource : NSDraggingSource, IPlatformDragSource
  24. {
  25. private const string NSPasteboardTypeString = "public.utf8-plain-text";
  26. private const string NSPasteboardTypeFileUrl = "public.file-url";
  27. private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>();
  28. private readonly IInputManager _inputManager;
  29. private DragDropEffects _allowedEffects;
  30. public override bool IgnoreModifierKeysWhileDragging => false;
  31. public DragSource()
  32. {
  33. _inputManager = AvaloniaLocator.Current.GetService<IInputManager>();
  34. }
  35. private string DataFormatToUTI(string s)
  36. {
  37. if (s == DataFormats.FileNames)
  38. return NSPasteboardTypeFileUrl;
  39. if (s == DataFormats.Text)
  40. return NSPasteboardTypeString;
  41. return s;
  42. }
  43. private NSDraggingItem CreateDraggingItem(string format, object data)
  44. {
  45. var pasteboardItem = new NSPasteboardItem();
  46. NSData nsData;
  47. if (data is string s)
  48. {
  49. if (format == DataFormats.FileNames)
  50. s = new Uri(s).AbsoluteUri; // Ensure file uris...
  51. nsData = NSData.FromString(s);
  52. }
  53. else if (data is Stream strm)
  54. nsData = NSData.FromStream(strm);
  55. else if (data is byte[] bytes)
  56. nsData = NSData.FromArray(bytes);
  57. else
  58. {
  59. BinaryFormatter bf = new BinaryFormatter();
  60. using (var ms = new MemoryStream())
  61. {
  62. bf.Serialize(ms, data);
  63. ms.Position = 0;
  64. nsData = NSData.FromStream(ms);
  65. }
  66. }
  67. pasteboardItem.SetDataForType(nsData, DataFormatToUTI(format));
  68. NSPasteboardWriting writing = new NSPasteboardWriting(pasteboardItem.Handle);
  69. return new NSDraggingItem(writing);
  70. }
  71. public IEnumerable<NSDraggingItem> CreateDraggingItems(string format, object data)
  72. {
  73. if (format == DataFormats.FileNames && data is IEnumerable<string> files)
  74. {
  75. foreach (var file in files)
  76. yield return CreateDraggingItem(format, file);
  77. yield break;
  78. }
  79. yield return CreateDraggingItem(format, data);
  80. }
  81. public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects)
  82. {
  83. // We need the TopLevelImpl + a mouse location so we just wait for the next event.
  84. var mouseEv = await _inputManager.PreProcess.OfType<RawMouseEventArgs>().FirstAsync();
  85. var view = ((mouseEv.Root as TopLevel)?.PlatformImpl as TopLevelImpl)?.View;
  86. if (view == null)
  87. return DragDropEffects.None;
  88. // Prepare the source event:
  89. var pt = view.TranslateLocalPoint(mouseEv.Position).ToMonoMacPoint();
  90. var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0);
  91. _allowedEffects = allowedEffects;
  92. var items = data.GetDataFormats().SelectMany(fmt => CreateDraggingItems(fmt, data.Get(fmt))).ToArray();
  93. view.BeginDraggingSession(items ,ev, this);
  94. return await _result;
  95. }
  96. public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag)
  97. {
  98. return DraggingInfo.ConvertDragOperation(_allowedEffects);
  99. }
  100. public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation)
  101. {
  102. _result.OnNext(DraggingInfo.ConvertDragOperation(operation));
  103. _result.OnCompleted();
  104. }
  105. }
  106. }