Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/xwt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBret Johnson <bret.johnson@microsoft.com>2022-04-01 21:47:59 +0300
committerGitHub <noreply@github.com>2022-04-01 21:47:59 +0300
commit54bf3faa58f3474670e732136d3bd54e9d78c168 (patch)
treec3001b7c0cebb23a5fbb7de5b568d971d8c03af0
parentbdfba7a36c3bf797043d8726510dc1a7b5ceb8dd (diff)
Fix drag/drop support (#1091)
Fix so that drag/drop support works properly with the Cocoa backend. This PR includes these fixes: DraggingUpdate passed the wrong coordinates. Now do the proper conversion from view coordinates to window coordinates, as expected by the Xwt client. Get the INSDraggingInfo object in the proper way, with Runtime.GetINativeObject<INSDraggingInfo> (dragInfo, owns: false), same as we do in other code now. Use the newer NSView.BeginDraggingSession API instead of the deprecated NSView.DragImage. This is needed so that we can provide a DraggingSource object in order to be notified when the drag/drop is over (no matter if successful or not) and call the Finished callback, as expected by Xwt client code. Populate the pasteboard item used for the drag properly, supporting text drag/drop and internal formats (normally a serialized object where the Id is the assembly qualified type name). To my knowledge the Android Designer is the only client that still uses this drag/drop code. All of these fixes were to make Android Designer drag/drop work properly. Squashed commits: * Update InitPasteboard, including all data types * Treat as internal data * Update INSDraggingInfo type, thanks to Sandy * More fixes * Fix DraggingUpdated coordinates to be widget, not window * Serialize drag data for Cocoa backend This more closely matches what was done on Gtk before. It’s needed for at least the Android Designer, supporting drag/drop on the AndroidDesignerDragData serializable type. * Use BeginDraggingSession for drag/drop This allows sending the OnDragFinished notification when the drag/drop is complete, in turn calling DragOperation.Finished. Calling Finished is expected for Xwt - it did before for Gtk and this makes the Cocoa backend behave the same. * Update pasteboard handling * Only use valid names for pasteboard types * Switch to using INSDraggingSource * Spaces -> tabs
-rw-r--r--Xwt.XamMac/Xwt.Mac/ViewBackend.cs142
1 files changed, 119 insertions, 23 deletions
diff --git a/Xwt.XamMac/Xwt.Mac/ViewBackend.cs b/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
index 68dd61e3..05fcf8f8 100644
--- a/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/ViewBackend.cs
@@ -581,18 +581,21 @@ namespace Xwt.Mac
public void DragStart (DragStartData sdata)
{
- var lo = Widget.ConvertPointToBase (new CGPoint (Widget.Bounds.X, Widget.Bounds.Y));
- lo = Widget.Window.ConvertBaseToScreen (lo);
- var ml = NSEvent.CurrentMouseLocation;
- var pb = NSPasteboard.FromName (NSPasteboard.NSDragPasteboardName);
- if (pb == null)
- throw new InvalidOperationException ("Could not get pasteboard");
if (sdata.Data == null)
throw new ArgumentNullException ("data");
- InitPasteboard (pb, sdata.Data);
+
+ NSPasteboardItem pasteboardItem = CreatePasteboardItem(sdata.Data);
+
+ var dragItem = new NSDraggingItem(pasteboardItem);
+
+ // Note that the hotspot (sdata.HotX, sdata.HotY) isn't currently supported here, but
+ // current users of this API don't use that anyway
var img = (NSImage)sdata.ImageBackend;
- var pos = new CGPoint (ml.X - lo.X - (float)sdata.HotX, lo.Y - ml.Y - (float)sdata.HotY + img.Size.Height);
- Widget.DragImage (img, pos, new CGSize (0, 0), NSApplication.SharedApplication.CurrentEvent, pb, Widget, true);
+ var frame = new CGRect(0, 0, img.Size.Width, img.Size.Height);
+ dragItem.SetDraggingFrame(frame, img);
+
+ var draggingSource = new DraggingSource (this);
+ Widget.BeginDraggingSession(new[] { dragItem }, NSApplication.SharedApplication.CurrentEvent, draggingSource);
}
public void SetDragSource (TransferDataType[] types, DragDropAction dragAction)
@@ -602,7 +605,7 @@ namespace Xwt.Mac
public void SetDragTarget (TransferDataType[] types, DragDropAction dragAction)
{
SetupForDragDrop (Widget.GetType ());
- var dtypes = types.Select (ToNSDragType).ToArray ();
+ var dtypes = types.Select (ToNSPasteboardType).ToArray ();
Widget.RegisterForDraggedTypes (dtypes);
}
@@ -617,10 +620,10 @@ namespace Xwt.Mac
if (ob == null)
return NSDragOperation.None;
var backend = ob.Backend;
-
- INSDraggingInfo di = (INSDraggingInfo) Runtime.GetNSObject (dragInfo);
+
+ var di = Runtime.GetINativeObject<INSDraggingInfo> (dragInfo, owns: false);
var types = di.DraggingPasteboard.Types.Select (ToXwtDragType).ToArray ();
- var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
+ var pos = backend.Widget.ConvertPointFromView (di.DraggingLocation, null).ToXwtPoint ();
if ((backend.currentEvents & WidgetEvent.DragOverCheck) != 0) {
var args = new DragOverCheckEventArgs (pos, types, ConvertAction (di.DraggingSourceOperationMask));
@@ -663,8 +666,8 @@ namespace Xwt.Mac
return false;
var backend = ob.Backend;
-
- INSDraggingInfo di = (INSDraggingInfo) Runtime.GetNSObject (dragInfo);
+
+ var di = Runtime.GetINativeObject<INSDraggingInfo> (dragInfo, owns: false);
var types = di.DraggingPasteboard.Types.Select (ToXwtDragType).ToArray ();
var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
@@ -687,7 +690,7 @@ namespace Xwt.Mac
var backend = ob.Backend;
- INSDraggingInfo di = (INSDraggingInfo) Runtime.GetNSObject (dragInfo);
+ var di = Runtime.GetINativeObject<INSDraggingInfo> (dragInfo, owns: false);
var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
if ((backend.currentEvents & WidgetEvent.DragDrop) != 0) {
@@ -720,16 +723,39 @@ namespace Xwt.Mac
eventSink.OnDragOver (args);
});
}
-
- void InitPasteboard (NSPasteboard pb, TransferDataSource data)
+
+ public virtual void OnDragFinished (DragFinishedEventArgs args)
+ {
+ ApplicationContext.InvokeUserCode(delegate {
+ eventSink.OnDragFinished (args);
+ });
+ }
+
+ NSPasteboardItem CreatePasteboardItem (TransferDataSource data)
{
- pb.ClearContents ();
+ var typesSupported = new List<string>();
+
foreach (var t in data.DataTypes) {
+ // Support dragging text internally and externally
if (t == TransferDataType.Text) {
- pb.AddTypes (new string[] { NSPasteboard.NSStringType }, null);
- pb.SetStringForType ((string)data.GetValue (t), NSPasteboard.NSStringType);
+ typesSupported.Add(NSPasteboard.NSPasteboardTypeString);
+ }
+ // For other well known types, we don't currently support dragging them
+ else if (t == TransferDataType.Uri || t == TransferDataType.Image || t == TransferDataType.Rtf || t == TransferDataType.Html) {
+ ;
+ }
+ // For internal types, provided serialized data
+ else {
+ typesSupported.Add(ToNSPasteboardType(t));
}
}
+
+ var pasteboardItem = new NSPasteboardItem();
+
+ var dataProvider = new PasteboardDataProvider(data);
+ pasteboardItem.SetDataProviderForTypes(dataProvider, typesSupported.ToArray());
+
+ return pasteboardItem;
}
static void FillDataStore (TransferDataStore store, NSPasteboard pb, string[] types)
@@ -775,14 +801,25 @@ namespace Xwt.Mac
return res;
}
- static string ToNSDragType (TransferDataType type)
+ public static string ToNSPasteboardType (TransferDataType type)
{
if (type == TransferDataType.Text) return NSPasteboard.NSStringType;
if (type == TransferDataType.Uri) return NSPasteboard.NSFilenamesType;
if (type == TransferDataType.Image) return NSPasteboard.NSPictType;
if (type == TransferDataType.Rtf) return NSPasteboard.NSRtfType;
if (type == TransferDataType.Html) return NSPasteboard.NSHtmlType;
- return type.Id;
+
+ // Internal types often use an assembly qualified name for the id, which
+ // apparently isn't valid for the pasteboard typename. In that case,
+ // remove everything after the ", " so that just the class full name
+ // forms the pasteboard type name (alphanumeric + periods, no spaces).
+ string pasteboardType = type.Id;
+ int delimiterIndex = pasteboardType.IndexOf(", ");
+ if (delimiterIndex != -1 && delimiterIndex > 0) {
+ pasteboardType = pasteboardType.Substring(0, delimiterIndex);
+ }
+
+ return pasteboardType;
}
static TransferDataType ToXwtDragType (string type)
@@ -883,5 +920,64 @@ namespace Xwt.Mac
child.Frame = new CGRect ((nfloat)cx, (nfloat)cy, (nfloat)cwidth, (nfloat)cheight);
}
}
+
+ class DraggingSource : NSObject, INSDraggingSource
+ {
+ WeakReference<ViewBackend> weakViewBackend;
+
+ public DraggingSource(ViewBackend viewBackend)
+ {
+ weakViewBackend = new WeakReference<ViewBackend>(viewBackend);
+ }
+
+ [Export("draggingSession:willBeginAtPoint:")]
+ public void DraggingSessionWillBeginAtPoint(NSDraggingSession session, CGPoint screenPoint)
+ {
+ }
+
+ [Export("draggingSession:endedAtPoint:operation:")]
+ public void DraggingSessionEndedAtPoint(NSDraggingSession session, CGPoint screenPoint, NSDragOperation operation)
+ {
+ if (weakViewBackend.TryGetTarget(out var viewBackend))
+ {
+ bool deleteSource = operation == NSDragOperation.Move || operation == NSDragOperation.Delete;
+ var args = new DragFinishedEventArgs(deleteSource);
+
+ viewBackend.OnDragFinished(args);
+ }
+ }
+ }
+
+ public class PasteboardDataProvider : NSObject, INSPasteboardItemDataProvider
+ {
+ TransferDataSource dataSource;
+
+ public PasteboardDataProvider(TransferDataSource dataSource)
+ {
+ this.dataSource = dataSource;
+ }
+
+ public void ProvideDataForType (NSPasteboard pasteboard, NSPasteboardItem item, string type)
+ {
+ foreach (TransferDataType transferDataType in dataSource.DataTypes) {
+ string currentPasteboardType = ViewBackend.ToNSPasteboardType (transferDataType);
+ if (currentPasteboardType == type) {
+ if (transferDataType == TransferDataType.Text) {
+ pasteboard.SetStringForType((string)dataSource.GetValue(transferDataType), NSPasteboard.NSPasteboardTypeString);
+ }
+ else {
+ // For internal types, provided serialized data
+ object value = dataSource.GetValue(transferDataType);
+ NSData serializedData = NSData.FromArray(TransferDataSource.SerializeValue(value));
+ pasteboard.SetDataForType(serializedData, type);
+ }
+ }
+ }
+ }
+
+ public void FinishedWithDataProvider (NSPasteboard pasteboard)
+ {
+ }
+ }
}