Browse Source

Throw ObjectDisposedException for ClipboardImpl/ClipboardDataObject (#17472)

pull/17483/head
Julien Lebosquain 1 year ago
committed by GitHub
parent
commit
337a56f140
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 69
      src/Avalonia.Native/ClipboardImpl.cs

69
src/Avalonia.Native/ClipboardImpl.cs

@ -1,4 +1,6 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -9,13 +11,12 @@ using Avalonia.Logging;
using Avalonia.Native.Interop;
using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
using MicroCom.Runtime;
namespace Avalonia.Native
{
class ClipboardImpl : IClipboard, IDisposable
{
private IAvnClipboard _native;
private IAvnClipboard? _native;
// TODO hide native types behind IAvnClipboard abstraction, so managed side won't depend on macOS.
private const string NSPasteboardTypeString = "public.utf8-plain-text";
@ -26,25 +27,30 @@ namespace Avalonia.Native
_native = native;
}
private IAvnClipboard Native
=> _native ?? throw new ObjectDisposedException(nameof(ClipboardImpl));
public Task ClearAsync()
{
_native.Clear();
Native.Clear();
return Task.CompletedTask;
}
public Task<string> GetTextAsync()
public Task<string?> GetTextAsync()
{
using (var text = _native.GetText(NSPasteboardTypeString))
return Task.FromResult(text.String);
using (var text = Native.GetText(NSPasteboardTypeString))
return Task.FromResult<string?>(text.String);
}
public unsafe Task SetTextAsync(string text)
public Task SetTextAsync(string? text)
{
_native.Clear();
var native = Native;
native.Clear();
if (text != null)
_native.SetText(NSPasteboardTypeString, text);
native.SetText(NSPasteboardTypeString, text);
return Task.CompletedTask;
}
@ -52,7 +58,7 @@ namespace Avalonia.Native
public IEnumerable<string> GetFormats()
{
var rv = new HashSet<string>();
using (var formats = _native.ObtainFormats())
using (var formats = Native.ObtainFormats())
{
var cnt = formats.Count;
for (uint c = 0; c < cnt; c++)
@ -81,27 +87,31 @@ namespace Avalonia.Native
_native = null;
}
public IEnumerable<string> GetFileNames()
public IEnumerable<string>? GetFileNames()
{
using (var strings = _native.GetStrings(NSFilenamesPboardType))
using (var strings = Native.GetStrings(NSFilenamesPboardType))
return strings?.ToStringArray();
}
public IEnumerable<IStorageItem> GetFiles()
public IEnumerable<IStorageItem>? GetFiles()
{
var storageApi = (StorageProviderApi)AvaloniaLocator.Current.GetRequiredService<IStorageProviderFactory>();
// TODO: use non-deprecated AppKit API to get NSUri instead of file names.
return GetFileNames()?
var fileNames = GetFileNames();
if (fileNames is null)
return null;
return fileNames
.Select(f => StorageProviderHelpers.TryGetUriFromFilePath(f, false) is { } uri
? storageApi.TryGetStorageItem(uri)
: null)
.Where(f => f is not null);
.Where(f => f is not null)!;
}
public unsafe Task SetDataObjectAsync(IDataObject data)
{
_native.Clear();
Native.Clear();
// If there is multiple values with the same "to" format, prefer these that were not mapped.
var formats = data.GetDataFormats().Select(f =>
@ -125,26 +135,26 @@ namespace Avalonia.Native
switch (o)
{
case string s:
_native.SetText(toFormat, s);
Native.SetText(toFormat, s);
break;
case IEnumerable<IStorageItem> storageItems:
using (var strings = new AvnStringArray(storageItems
.Select(s => s.TryGetLocalPath())
.Where(p => p is not null)))
{
_native.SetStrings(toFormat, strings);
Native.SetStrings(toFormat, strings);
}
break;
case IEnumerable<string> managedStrings:
using (var strings = new AvnStringArray(managedStrings))
{
_native.SetStrings(toFormat, strings);
Native.SetStrings(toFormat, strings);
}
break;
case byte[] bytes:
{
fixed (byte* pbytes = bytes)
_native.SetBytes(toFormat, pbytes, bytes.Length);
Native.SetBytes(toFormat, pbytes, bytes.Length);
break;
}
default:
@ -161,7 +171,7 @@ namespace Avalonia.Native
return Task.FromResult(GetFormats().ToArray());
}
public async Task<object> GetDataAsync(string format)
public async Task<object?> GetDataAsync(string format)
{
if (format == DataFormats.Text || format == NSPasteboardTypeString)
return await GetTextAsync();
@ -169,15 +179,15 @@ namespace Avalonia.Native
return GetFileNames();
if (format == DataFormats.Files)
return GetFiles();
using (var n = _native.GetBytes(format))
using (var n = Native.GetBytes(format))
return n.Bytes;
}
}
class ClipboardDataObject : IDataObject, IDisposable
{
private ClipboardImpl _clipboard;
private List<string> _formats;
private ClipboardImpl? _clipboard;
private List<string>? _formats;
public ClipboardDataObject(IAvnClipboard clipboard)
{
@ -190,14 +200,17 @@ namespace Avalonia.Native
_clipboard = null;
}
List<string> Formats => _formats ??= _clipboard.GetFormats().ToList();
private ClipboardImpl Clipboard
=> _clipboard ?? throw new ObjectDisposedException(nameof(ClipboardDataObject));
private List<string> Formats => _formats ??= Clipboard.GetFormats().ToList();
public IEnumerable<string> GetDataFormats() => Formats;
public bool Contains(string dataFormat) => Formats.Contains(dataFormat);
public object Get(string dataFormat) => _clipboard.GetDataAsync(dataFormat).GetAwaiter().GetResult();
public object? Get(string dataFormat) => Clipboard.GetDataAsync(dataFormat).GetAwaiter().GetResult();
public Task SetFromDataObjectAsync(IDataObject dataObject) => _clipboard.SetDataObjectAsync(dataObject);
public Task SetFromDataObjectAsync(IDataObject dataObject) => Clipboard.SetDataObjectAsync(dataObject);
}
}

Loading…
Cancel
Save