diff --git a/Avalonia.sln b/Avalonia.sln index a0314b1c33..768fb50452 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -37,11 +37,12 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + src\Shared\CallerArgumentExpressionAttribute.cs = src\Shared\CallerArgumentExpressionAttribute.cs src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs - src\Shared\NullableAttributes.cs = src\Shared\NullableAttributes.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs src\Shared\StringCompatibilityExtensions.cs = src\Shared\StringCompatibilityExtensions.cs + src\Shared\StreamCompatibilityExtensions.cs = src\Shared\StreamCompatibilityExtensions.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 35d8c92717..4df9a900be 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,4 +1,4 @@ - + @@ -133,4 +133,76 @@ baseline/netstandard2.0/Avalonia.Controls.dll target/netstandard2.0/Avalonia.Controls.dll - \ No newline at end of file + + CP0006 + M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetDataAsync + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataAsync + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IPlatformDragSource.DoDragDropAsync(Avalonia.Input.PointerEventArgs,Avalonia.Input.IDataTransfer,Avalonia.Input.DragDropEffects) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetDataAsync + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataAsync + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IPlatformDragSource.DoDragDropAsync(Avalonia.Input.PointerEventArgs,Avalonia.Input.IDataTransfer,Avalonia.Input.DragDropEffects) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetDataAsync + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataAsync + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + + + CP0006 + M:Avalonia.Input.Platform.IPlatformDragSource.DoDragDropAsync(Avalonia.Input.PointerEventArgs,Avalonia.Input.IDataTransfer,Avalonia.Input.DragDropEffects) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + + diff --git a/native/Avalonia.Native/inc/com.h b/native/Avalonia.Native/inc/com.h index 42a989e050..9a3f067dce 100644 --- a/native/Avalonia.Native/inc/com.h +++ b/native/Avalonia.Native/inc/com.h @@ -28,7 +28,8 @@ typedef DWORD ULONG; #define E_UNEXPECTED 0x8000FFFFL #define E_HANDLE 0x80070006L #define E_INVALIDARG 0x80070057L -#define COR_E_INVALIDOPERATION 0x80131509L +#define COR_E_INVALIDOPERATION 0x80131509L +#define COR_E_OBJECTDISPOSED 0x80131622L struct IUnknown { diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 9a67ee0161..868551eac1 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; }; F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = F10084832BFF1F9E0024303E /* TopLevelImpl.h */; }; F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = F10084852BFF1FB40024303E /* TopLevelImpl.mm */; }; + F931F8682E2D43A7004E081E /* clipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F931F8672E2D43A4004E081E /* clipboard.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -130,6 +131,7 @@ EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = ""; }; F10084832BFF1F9E0024303E /* TopLevelImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLevelImpl.h; sourceTree = ""; }; F10084852BFF1FB40024303E /* TopLevelImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TopLevelImpl.mm; sourceTree = ""; }; + F931F8672E2D43A4004E081E /* clipboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -189,6 +191,7 @@ 1AFD334023E03C4F0042899B /* controlhost.mm */, 5BF943652167AD1D009CAE35 /* cursor.h */, 5B21A981216530F500CEE36E /* cursor.mm */, + F931F8672E2D43A4004E081E /* clipboard.h */, 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */, 1A465D0F246AB61600C5858B /* dnd.mm */, AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */, @@ -246,6 +249,7 @@ files = ( 37155CE4233C00EB0034DCE9 /* menu.h in Headers */, F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */, + F931F8682E2D43A7004E081E /* clipboard.h in Headers */, BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */, 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */, 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */, diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 4cc495f321..6fe1c8ceeb 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -868,7 +868,7 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt) return NSDragOperationNone; int reffects = (int)parent->TopLevelEvents ->DragEvent(type, point, modifiers, effects, - CreateClipboard([info draggingPasteboard], nil), + CreateClipboard([info draggingPasteboard]), GetAvnDataObjectHandleFromDraggingInfo(info)); NSDragOperation ret = static_cast(0); diff --git a/native/Avalonia.Native/src/OSX/StorageProvider.mm b/native/Avalonia.Native/src/OSX/StorageProvider.mm index 92278a85e9..4a82eeaea7 100644 --- a/native/Avalonia.Native/src/OSX/StorageProvider.mm +++ b/native/Avalonia.Native/src/OSX/StorageProvider.mm @@ -355,6 +355,35 @@ public: } } + virtual HRESULT TryResolveFileReferenceUri(IAvnString* fileUriStr, IAvnString** ret) override { + if (ret == nullptr) + return E_POINTER; + + if (fileUriStr == nullptr) + { + *ret = nullptr; + return S_OK; + } + + auto fileUri = [NSURL URLWithString:GetNSStringAndRelease(fileUriStr)]; + if (fileUri == nil) + { + *ret = nullptr; + return S_OK; + } + + auto filePathUri = [fileUri filePathURL]; + if (fileUri == nil) + { + *ret = nullptr; + return S_OK; + } + + *ret = CreateAvnString([filePathUri absoluteString]); + return S_OK; + } + + private: NSView* CreateAccessoryView() { // The label. Add attributes per-OS to match the labels that macOS uses. diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.h b/native/Avalonia.Native/src/OSX/TopLevelImpl.h index b18ad21d30..0be1439ec2 100644 --- a/native/Avalonia.Native/src/OSX/TopLevelImpl.h +++ b/native/Avalonia.Native/src/OSX/TopLevelImpl.h @@ -61,8 +61,13 @@ public: virtual HRESULT GetCurrentDisplayId (CGDirectDisplayID* ret) override; - virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, - IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) override; + virtual HRESULT BeginDragAndDropOperation( + AvnDragDropEffects effects, + AvnPoint point, + IAvnClipboardDataSource* source, + IAvnDndResultCallback* callback, + void* sourceHandle) override; + protected: NSCursor *cursor; virtual void UpdateAppearance(); diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.mm b/native/Avalonia.Native/src/OSX/TopLevelImpl.mm index c247e48fe0..bdfc1be62f 100644 --- a/native/Avalonia.Native/src/OSX/TopLevelImpl.mm +++ b/native/Avalonia.Native/src/OSX/TopLevelImpl.mm @@ -7,6 +7,7 @@ #include "AvnTextInputMethod.h" #include "AvnView.h" #include "common.h" +#include "clipboard.h" TopLevelImpl::~TopLevelImpl() { View = nullptr; @@ -272,13 +273,15 @@ void TopLevelImpl::UpdateAppearance() { } -HRESULT TopLevelImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) { +HRESULT TopLevelImpl::BeginDragAndDropOperation( + AvnDragDropEffects effects, + AvnPoint point, + IAvnClipboardDataSource* source, + IAvnDndResultCallback* callback, + void* sourceHandle) +{ START_COM_CALL; - auto item = TryGetPasteboardItem(clipboard); - [item setString:@"" forType:GetAvnCustomDataType()]; - if (item == nil) - return E_INVALIDARG; if (View == NULL) return E_FAIL; @@ -301,11 +304,19 @@ HRESULT TopLevelImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnP } } - auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item]; - + auto itemCount = source->GetItemCount(); + auto draggingItems = [NSMutableArray arrayWithCapacity:itemCount]; auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height}; - [dragItem setDraggingFrame:dragItemRect contents:dragItemImage]; + + for (auto i = 0; i < itemCount; ++i) + { + auto item = source->GetItem(i); + auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source]; + auto draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:writeableItem]; + [draggingItem setDraggingFrame:dragItemRect contents:dragItemImage]; + [draggingItems addObject:draggingItem]; + } int op = 0; int ieffects = (int) effects; @@ -315,8 +326,10 @@ HRESULT TopLevelImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnP op |= NSDragOperationLink; if ((ieffects & (int) AvnDragDropEffects::Move) != 0) op |= NSDragOperationMove; - [View beginDraggingSessionWithItems:@[dragItem] event:nsevent - source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; + + [View beginDraggingSessionWithItems:draggingItems + event:nsevent + source:CreateDraggingSource((NSDragOperation) op, callback, sourceHandle)]; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/clipboard.h b/native/Avalonia.Native/src/OSX/clipboard.h new file mode 100644 index 0000000000..fd0ddbf117 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/clipboard.h @@ -0,0 +1,7 @@ +#pragma once + +#include "common.h" + +@interface WriteableClipboardItem : NSObject +- (nonnull instancetype) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source; +@end diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index 68f0e7d87a..45770f8c1e 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -1,206 +1,295 @@ +#import #include "common.h" +#include "clipboard.h" #include "AvnString.h" class Clipboard : public ComSingleObject { private: - NSPasteboard* _pb; - NSPasteboardItem* _item; + NSPasteboard* _pasteboard; public: FORWARD_IUNKNOWN() - Clipboard(NSPasteboard* pasteboard, NSPasteboardItem* item) + Clipboard(NSPasteboard* pasteboard) { - if(pasteboard == nil && item == nil) + if (pasteboard == nil) pasteboard = [NSPasteboard generalPasteboard]; - _pb = pasteboard; - _item = item; + _pasteboard = pasteboard; } - NSPasteboardItem* TryGetItem() + virtual HRESULT GetFormats(int64_t changeCount, IAvnStringArray** ret) override { - return _item; + START_COM_ARP_CALL; + + if (ret == nullptr) + return E_POINTER; + + if (changeCount != [_pasteboard changeCount]) + return COR_E_OBJECTDISPOSED; + + auto types = [_pasteboard types]; + *ret = types == nil ? nullptr : CreateAvnStringArray(types); + return S_OK; } - - virtual HRESULT GetText (char* type, IAvnString**ppv) override + + virtual HRESULT GetItemCount(int64_t changeCount, int* ret) override { - START_COM_CALL; + START_COM_ARP_CALL; - @autoreleasepool - { - if(ppv == nullptr) - { - return E_POINTER; - } - NSString* typeString = [NSString stringWithUTF8String:(const char*)type]; - NSString* string = _item == nil ? [_pb stringForType:typeString] : [_item stringForType:typeString]; - - *ppv = CreateAvnString(string); - - return S_OK; - } + if (ret == nullptr) + return E_POINTER; + + if (changeCount != [_pasteboard changeCount]) + return COR_E_OBJECTDISPOSED; + + auto items = [_pasteboard pasteboardItems]; + *ret = items == nil ? 0 : (int)[items count]; + return S_OK; } - virtual HRESULT SetStrings(char* type, IAvnStringArray*ppv) override + virtual HRESULT GetItemFormats(int index, int64_t changeCount, IAvnStringArray** ret) override { - START_COM_CALL; + START_COM_ARP_CALL; - @autoreleasepool - { - NSArray* data = GetNSArrayOfStringsAndRelease(ppv); - NSString* typeString = [NSString stringWithUTF8String:(const char*)type]; - if(_item == nil) - [_pb setPropertyList: data forType: typeString]; - else - [_item setPropertyList: data forType:typeString]; - return S_OK; - } + if (ret == nullptr) + return E_POINTER; + + if (changeCount != [_pasteboard changeCount]) + return COR_E_OBJECTDISPOSED; + + auto item = [[_pasteboard pasteboardItems] objectAtIndex:index]; + auto types = [item types]; + *ret = types == nil ? nullptr : CreateAvnStringArray(types); + return S_OK; } - virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override + virtual HRESULT GetItemValueAsString(int index, int64_t changeCount, const char* format, IAvnString** ret) override { - START_COM_CALL; + START_COM_ARP_CALL; - @autoreleasepool - { - *ppv= nil; - NSString* typeString = [NSString stringWithUTF8String:(const char*)type]; - NSObject* data = _item == nil ? [_pb propertyListForType: typeString] : [_item propertyListForType: typeString]; - if(data == nil) - return S_OK; - - if([data isKindOfClass: [NSString class]]) - { - *ppv = CreateAvnStringArray((NSString*) data); - return S_OK; - } - - NSArray* arr = (NSArray*)data; - - for(int c = 0; c < [arr count]; c++) - if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]]) - return E_INVALIDARG; - - *ppv = CreateAvnStringArray(arr); - return S_OK; - } + if (ret == nullptr) + return E_POINTER; + + if (changeCount != [_pasteboard changeCount]) + return COR_E_OBJECTDISPOSED; + + auto item = [[_pasteboard pasteboardItems] objectAtIndex:index]; + auto value = [item stringForType:[NSString stringWithUTF8String:format]]; + *ret = value == nil ? nullptr : CreateAvnString(value); + return S_OK; } - virtual HRESULT SetText (char* type, char* utf8String) override + virtual HRESULT GetItemValueAsBytes(int index, int64_t changeCount, const char* format, IAvnString** ret) override { - START_COM_CALL; + START_COM_ARP_CALL; - @autoreleasepool - { - auto string = [NSString stringWithUTF8String:(const char*)utf8String]; - auto typeString = [NSString stringWithUTF8String:(const char*)type]; - if(_item == nil) - [_pb setString: string forType: typeString]; - else - [_item setString: string forType:typeString]; + if (ret == nullptr) + return E_POINTER; - return S_OK; - } + if (changeCount != [_pasteboard changeCount]) + return COR_E_OBJECTDISPOSED; + + auto item = [[_pasteboard pasteboardItems] objectAtIndex:index]; + auto value = [item dataForType:[NSString stringWithUTF8String:format]]; + + *ret = value == nil || [value length] == 0 + ? nullptr + : CreateByteArray((void*)[value bytes], (int)[value length]); + return S_OK; + } + + virtual HRESULT Clear(int64_t* ret) override + { + START_COM_ARP_CALL; + + *ret = [_pasteboard clearContents]; + return S_OK; } - virtual HRESULT SetBytes(char* type, void* bytes, int len) override + virtual HRESULT GetChangeCount(int64_t* ret) override { - START_COM_CALL; + START_COM_ARP_CALL; - @autoreleasepool + *ret = [_pasteboard changeCount]; + return S_OK; + } + + virtual HRESULT SetData(IAvnClipboardDataSource* source) override + { + START_COM_ARP_CALL; + + auto count = source->GetItemCount(); + auto writeableItems = [NSMutableArray arrayWithCapacity:count]; + + for (auto i = 0; i < count; ++i) { - auto typeString = [NSString stringWithUTF8String:(const char*)type]; - auto data = [NSData dataWithBytes:bytes length:len]; - if(_item == nil) - [_pb setData:data forType:typeString]; - else - [_item setData:data forType:typeString]; - return S_OK; + auto item = source->GetItem(i); + auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source]; + [writeableItems addObject:writeableItem]; } + + [_pasteboard writeObjects:writeableItems]; + return S_OK; } - - virtual HRESULT GetBytes(char* type, IAvnString**ppv) override + + virtual bool IsTextFormat(const char *format) override { - START_COM_CALL; + START_COM_ARP_CALL; + + auto formatString = [NSString stringWithUTF8String:format]; - @autoreleasepool + if (@available(macOS 11.0, *)) { - *ppv = nil; - auto typeString = [NSString stringWithUTF8String:(const char*)type]; - NSData*data; - @try - { - if(_item) - data = [_item dataForType:typeString]; - else - data = [_pb dataForType:typeString]; - if(data == nil) - return E_FAIL; - } - @catch(NSException* e) - { - return E_FAIL; - } - *ppv = CreateByteArray((void*)data.bytes, (int)data.length); - return S_OK; + auto type = [UTType typeWithIdentifier:formatString]; + return type != nil && [type conformsToType:UTTypeText]; + } + else + { + return UTTypeConformsTo((__bridge CFStringRef)formatString, kUTTypeText); } } +}; + +extern IAvnClipboard* CreateClipboard(NSPasteboard* pb) +{ + return new Clipboard(pb); +} + + +@implementation WriteableClipboardItem +{ + IAvnClipboardDataItem* _item; + IAvnClipboardDataSource* _source; +} + +- (nonnull WriteableClipboardItem*) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source +{ + self = [super init]; + _item = item; + _source = source; + + // Each item references its source so it doesn't get disposed too early. + source->AddRef(); + + return self; +} - virtual HRESULT Clear(int64_t* rv) override +NSString* TryConvertFormatToUti(NSString* format) +{ + if (@available(macOS 11.0, *)) { - START_COM_CALL; - - @autoreleasepool + auto type = [UTType typeWithIdentifier:format]; + if (type == nil) { - if(_item != nil) - { - _item = [NSPasteboardItem new]; - return 0; - } + if ([format containsString:@"/"]) + type = [UTType typeWithMIMEType:format]; else + type = [UTType exportedTypeWithIdentifier:format]; + + if (type == nil) { - *rv = [_pb clearContents]; - [_pb setString:@"" forType:NSPasteboardTypeString]; + // For now, we need to use the deprecated UTTypeCreatePreferredIdentifierForTag to create a dynamic UTI for arbitrary strings. + // This is only necessary because the old IDataObject can provide arbitrary types that aren't UTIs nor mime types. + // With the new DataFormat: + // - If the format is an application format, the managed side provides a UTI like net.avaloniaui.app.uti.xxx. + // - If the format is an OS format, the user has been warned that they MUST provide a name which is valid for the OS. + // TODO12: remove! + auto fromPasteboardType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (__bridge CFStringRef)format, nil); + if (fromPasteboardType != nil) + return (__bridge_transfer NSString*)fromPasteboardType; } - - return S_OK; } + + return type == nil ? nil : [type identifier]; } - - virtual HRESULT GetChangeCount(int64_t* rv) override + else { - START_COM_CALL; - if(_item == nil) - { - *rv = [_pb changeCount]; - return S_OK; - } - return E_NOTIMPL; + auto bridgedFormat = (__bridge CFStringRef)format; + if (UTTypeIsDeclared(bridgedFormat)) + return format; + + auto fromMimeType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, bridgedFormat, nil); + if (fromMimeType != nil) + return (__bridge_transfer NSString*)fromMimeType; + + auto fromPasteboardType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, bridgedFormat, nil); + if (fromPasteboardType != nil) + return (__bridge_transfer NSString*)fromPasteboardType; + + return nil; } +} + +- (nonnull NSArray*) writableTypesForPasteboard:(nonnull NSPasteboard*)pasteboard +{ + auto formats = _item->ProvideFormats(); + if (formats == nullptr) + return [NSArray array]; - virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override + auto count = formats->GetCount(); + if (count == 0) + return [NSArray array]; + + auto utis = [NSMutableArray arrayWithCapacity:count]; + IAvnString* format; + for (auto i = 0; i < count; ++i) { - START_COM_CALL; - - @autoreleasepool - { - *ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]); - return S_OK; - } + if (formats->Get(i, &format) != S_OK) + continue; + + // Only UTIs must be returned from writableTypesForPasteboard or an exception will be thrown + auto formatString = GetNSStringAndRelease(format); + auto uti = TryConvertFormatToUti(formatString); + if (uti != nil) + [utis addObject:uti]; } -}; + formats->Release(); + + [utis addObject:GetAvnCustomDataType()]; + + return utis; +} -extern IAvnClipboard* CreateClipboard(NSPasteboard* pb, NSPasteboardItem* item) +- (NSPasteboardWritingOptions) writingOptionsForType:(NSPasteboardType)type pasteboard:(NSPasteboard*)pasteboard { - return new Clipboard(pb, item); + return [type isEqualToString:NSPasteboardTypeString] || [type isEqualToString:GetAvnCustomDataType()] + ? 0 + : NSPasteboardWritingPromised; } -extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*cb) +- (nullable id) pasteboardPropertyListForType:(nonnull NSPasteboardType)type { - auto clipboard = dynamic_cast(cb); - if(clipboard == nil) + if ([type isEqualToString:GetAvnCustomDataType()]) + return @""; + + ComPtr value(_item->GetValue([type UTF8String]), true); + if (value.getRaw() == nullptr) return nil; - return clipboard->TryGetItem(); + + if (value->IsString()) + return GetNSStringAndRelease(value->AsString()); + + auto length = value->GetByteLength(); + auto buffer = malloc(length); + value->CopyBytesTo(buffer); + return [NSData dataWithBytesNoCopy:buffer length:length]; +} + +- (void) dealloc +{ + if (_item != nullptr) + { + _item->Release(); + _item = nullptr; + } + + if (_source != nullptr) + { + _source->Release(); + _source = nullptr; + } } + +@end diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 36c157704d..819254b841 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -16,8 +16,7 @@ extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); extern IAvnStorageProvider* CreateStorageProvider(); extern IAvnScreens* CreateScreens(IAvnScreenEvents* cb); -extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*); -extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*); +extern IAvnClipboard* CreateClipboard(NSPasteboard* pb); extern NSObject* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle); extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject* info); extern NSString* GetAvnCustomDataType(); diff --git a/native/Avalonia.Native/src/OSX/dnd.mm b/native/Avalonia.Native/src/OSX/dnd.mm index 531bdcccfd..aebe4afb88 100644 --- a/native/Avalonia.Native/src/OSX/dnd.mm +++ b/native/Avalonia.Native/src/OSX/dnd.mm @@ -14,9 +14,17 @@ extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop) extern NSString* GetAvnCustomDataType() { - char buffer[256]; - sprintf(buffer, "net.avaloniaui.inproc.uti.n%in", getpid()); - return [NSString stringWithUTF8String:buffer]; + static NSString* result = nil; + + if (result == nil) + { + const size_t bufferSize = 256; + char buffer[bufferSize]; + snprintf(buffer, bufferSize, "net.avaloniaui.inproc.uti.n%in", getpid()); + result = [NSString stringWithUTF8String:buffer]; + } + + return result; } @interface AvnDndSource : NSObject diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 0e3621517e..f4e244efdd 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -312,18 +312,7 @@ public: @autoreleasepool { - *ppv = ::CreateClipboard (nil, nil); - return S_OK; - } - } - - virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) override - { - START_COM_CALL; - - @autoreleasepool - { - *ppv = ::CreateClipboard (nil, [NSPasteboardItem new]); + *ppv = ::CreateClipboard(nil); return S_OK; } } diff --git a/samples/ControlCatalog/Pages/ClipboardPage.xaml b/samples/ControlCatalog/Pages/ClipboardPage.xaml index 0349936733..2c22348f6f 100644 --- a/samples/ControlCatalog/Pages/ClipboardPage.xaml +++ b/samples/ControlCatalog/Pages/ClipboardPage.xaml @@ -6,12 +6,10 @@