Added IClipboard:TryGetInProcessDataObjectAsyns (#18340)
* Implemented IClipboard.TryGetInProcessDataObjectAsync for X11 * [Win32] Implemented TryGetInProcessDataObjectAsync * Rest of the platforms * Run `nuke ValidateApiDiff` --------- Co-authored-by: Max Katz <maxkatz6@outlook.com>
This commit is contained in:
+12
-6
@@ -49,6 +49,18 @@
|
||||
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
|
||||
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Avalonia.Input.Platform.IClipboard.FlushAsync</Target>
|
||||
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
|
||||
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataObjectAsync</Target>
|
||||
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
|
||||
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFileAsync(System.String)</Target>
|
||||
@@ -61,12 +73,6 @@
|
||||
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
|
||||
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Avalonia.Input.Platform.IClipboard.FlushAsync</Target>
|
||||
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
|
||||
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:Avalonia.Controls.Notifications.IManagedNotificationManager.Close(System.Object)</Target>
|
||||
|
||||
@@ -148,17 +148,20 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual HRESULT Clear() override
|
||||
virtual HRESULT Clear(int64_t* rv) override
|
||||
{
|
||||
START_COM_CALL;
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
if(_item != nil)
|
||||
{
|
||||
_item = [NSPasteboardItem new];
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_pb clearContents];
|
||||
*rv = [_pb clearContents];
|
||||
[_pb setString:@"" forType:NSPasteboardTypeString];
|
||||
}
|
||||
|
||||
@@ -166,6 +169,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual HRESULT GetChangeCount(int64_t* rv) override
|
||||
{
|
||||
START_COM_CALL;
|
||||
if(_item == nil)
|
||||
{
|
||||
*rv = [_pb changeCount];
|
||||
return S_OK;
|
||||
}
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override
|
||||
{
|
||||
START_COM_CALL;
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
<Button Click="GetFormats" Content="Get clipboard formats" />
|
||||
<Button Click="Clear" Content="Clear clipboard" />
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Padding="0 0 5 0">Our DataObject is still on clipboard? <Run x:Name="OwnsClipboardDataObject"/></TextBlock>
|
||||
</StackPanel>
|
||||
<TextBox x:Name="ClipboardContent"
|
||||
MinHeight="100"
|
||||
AcceptsReturn="True"
|
||||
|
||||
@@ -3,13 +3,17 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Platform.Storage.FileIO;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace ControlCatalog.Pages
|
||||
{
|
||||
@@ -18,8 +22,13 @@ namespace ControlCatalog.Pages
|
||||
private INotificationManager? _notificationManager;
|
||||
private INotificationManager NotificationManager => _notificationManager
|
||||
??= new WindowNotificationManager(TopLevel.GetTopLevel(this)!);
|
||||
|
||||
private readonly DispatcherTimer _clipboardLastDataObjectChecker;
|
||||
private DataObject? _storedDataObject;
|
||||
public ClipboardPage()
|
||||
{
|
||||
_clipboardLastDataObjectChecker =
|
||||
new DispatcherTimer(TimeSpan.FromSeconds(0.5), default, CheckLastDataObject);
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -48,7 +57,7 @@ namespace ControlCatalog.Pages
|
||||
{
|
||||
if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
|
||||
{
|
||||
var dataObject = new DataObject();
|
||||
var dataObject = _storedDataObject = new DataObject();
|
||||
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
|
||||
await clipboard.SetDataObjectAsync(dataObject);
|
||||
}
|
||||
@@ -96,7 +105,7 @@ namespace ControlCatalog.Pages
|
||||
|
||||
if (files.Count > 0)
|
||||
{
|
||||
var dataObject = new DataObject();
|
||||
var dataObject = _storedDataObject = new DataObject();
|
||||
dataObject.Set(DataFormats.Files, files);
|
||||
await clipboard.SetDataObjectAsync(dataObject);
|
||||
NotificationManager.Show(new Notification("Success", "Copy completated.", NotificationType.Success));
|
||||
@@ -135,5 +144,38 @@ namespace ControlCatalog.Pages
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_clipboardLastDataObjectChecker.Start();
|
||||
base.OnAttachedToVisualTree(e);
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_clipboardLastDataObjectChecker.Stop();
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
private Run OwnsClipboardDataObject => this.Get<Run>("OwnsClipboardDataObject");
|
||||
private bool _checkingClipboardDataObject;
|
||||
private async void CheckLastDataObject(object? sender, EventArgs e)
|
||||
{
|
||||
if(_checkingClipboardDataObject)
|
||||
return;
|
||||
try
|
||||
{
|
||||
_checkingClipboardDataObject = true;
|
||||
var task = TopLevel.GetTopLevel(this)?.Clipboard?.TryGetInProcessDataObjectAsync();
|
||||
var owns = task != null && (await task) == _storedDataObject && _storedDataObject != null;
|
||||
OwnsClipboardDataObject.Text = owns ? "Yes" : "No";
|
||||
OwnsClipboardDataObject.Foreground = owns ? Brushes.Green : Brushes.Red;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_checkingClipboardDataObject = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace Avalonia.Android.Platform
|
||||
|
||||
public Task<object?> GetDataAsync(string format) => throw new PlatformNotSupportedException();
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
|
||||
@@ -49,5 +49,14 @@ namespace Avalonia.Input.Platform
|
||||
/// <param name="format">A string that specifies the format of the data to retrieve. For a set of predefined data formats, see the <see cref="DataFormats"/> class.</param>
|
||||
/// <returns></returns>
|
||||
Task<object?> GetDataAsync(string format);
|
||||
|
||||
/// <summary>
|
||||
/// If clipboard contains the IDataObject that was set by a previous call to <see cref="SetDataObjectAsync"/>,
|
||||
/// return said IDataObject instance. Otherwise, return null.
|
||||
/// Note that not every platform supports that method, on unsupported platforms this method will always return
|
||||
/// null
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<IDataObject?> TryGetInProcessDataObjectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +226,8 @@ namespace Avalonia.DesignerSupport.Remote
|
||||
|
||||
public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
|
||||
|
||||
public Task<IDataObject> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject>(null);
|
||||
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Avalonia.Native
|
||||
class ClipboardImpl : IClipboard, IDisposable
|
||||
{
|
||||
private IAvnClipboard? _native;
|
||||
private IDataObject? _savedDataObject;
|
||||
private long _lastClearChangeCount;
|
||||
|
||||
// TODO hide native types behind IAvnClipboard abstraction, so managed side won't depend on macOS.
|
||||
private const string NSPasteboardTypeString = "public.utf8-plain-text";
|
||||
@@ -30,10 +32,15 @@ namespace Avalonia.Native
|
||||
private IAvnClipboard Native
|
||||
=> _native ?? throw new ObjectDisposedException(nameof(ClipboardImpl));
|
||||
|
||||
private void ClearCore()
|
||||
{
|
||||
_savedDataObject = null;
|
||||
_lastClearChangeCount = Native.Clear();
|
||||
}
|
||||
|
||||
public Task ClearAsync()
|
||||
{
|
||||
Native.Clear();
|
||||
|
||||
ClearCore();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ namespace Avalonia.Native
|
||||
{
|
||||
var native = Native;
|
||||
|
||||
native.Clear();
|
||||
ClearCore();
|
||||
|
||||
if (text != null)
|
||||
native.SetText(NSPasteboardTypeString, text);
|
||||
@@ -83,6 +90,7 @@ namespace Avalonia.Native
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_savedDataObject = null;
|
||||
_native?.Dispose();
|
||||
_native = null;
|
||||
}
|
||||
@@ -111,7 +119,7 @@ namespace Avalonia.Native
|
||||
|
||||
public unsafe Task SetDataObjectAsync(IDataObject data)
|
||||
{
|
||||
Native.Clear();
|
||||
ClearCore();
|
||||
|
||||
// If there is multiple values with the same "to" format, prefer these that were not mapped.
|
||||
var formats = data.GetDataFormats().Select(f =>
|
||||
@@ -163,6 +171,8 @@ namespace Avalonia.Native
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_savedDataObject = data;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -183,9 +193,17 @@ namespace Avalonia.Native
|
||||
return n.Bytes;
|
||||
}
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync()
|
||||
{
|
||||
if (Native.ChangeCount != _lastClearChangeCount)
|
||||
_savedDataObject = null;
|
||||
return Task.FromResult(_savedDataObject);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
|
||||
}
|
||||
|
||||
class ClipboardDataObject : IDataObject, IDisposable
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@clr-access internal
|
||||
@clr-map bool int
|
||||
@clr-map u_int64_t ulong
|
||||
@clr-map int64_t long
|
||||
@clr-map long IntPtr
|
||||
@cpp-preamble @@
|
||||
#pragma once
|
||||
@@ -972,7 +973,8 @@ interface IAvnClipboard : IUnknown
|
||||
HRESULT SetBytes(char* type, void* utf8Text, int len);
|
||||
HRESULT GetBytes(char* type, IAvnString**ppv);
|
||||
|
||||
HRESULT Clear();
|
||||
HRESULT Clear(int64_t* ret);
|
||||
HRESULT GetChangeCount(int64_t* ret);
|
||||
}
|
||||
|
||||
[uuid(3f998545-f027-4d4d-bd2a-1a80926d984e)]
|
||||
|
||||
@@ -314,6 +314,13 @@ namespace Avalonia.X11
|
||||
return StoreAtomsInClipboardManager(data);
|
||||
}
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync()
|
||||
{
|
||||
if (XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) == _handle)
|
||||
return Task.FromResult(_storedDataObject);
|
||||
return Task.FromResult<IDataObject?>(null);
|
||||
}
|
||||
|
||||
public async Task<string[]> GetFormatsAsync()
|
||||
{
|
||||
if (!HasOwner)
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Avalonia.Browser
|
||||
public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
|
||||
|
||||
public Task<object?> GetDataAsync(string format) => Task.FromResult<object?>(null);
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
|
||||
@@ -17,66 +17,47 @@ namespace Avalonia.Headless
|
||||
{
|
||||
internal class HeadlessClipboardStub : IClipboard
|
||||
{
|
||||
private string? _text;
|
||||
private IDataObject? _data;
|
||||
|
||||
public Task<string?> GetTextAsync()
|
||||
{
|
||||
return Task.Run(() => _text);
|
||||
return Task.FromResult(_data?.GetText());
|
||||
}
|
||||
|
||||
public Task SetTextAsync(string? text)
|
||||
{
|
||||
return Task.Run(() => _text = text);
|
||||
var data = new DataObject();
|
||||
if (text != null)
|
||||
data.Set(DataFormats.Text, text);
|
||||
_data = data;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ClearAsync()
|
||||
{
|
||||
return Task.Run(() => _text = null);
|
||||
_data = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SetDataObjectAsync(IDataObject data)
|
||||
{
|
||||
return Task.Run(() => _data = data);
|
||||
_data = data;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<string[]> GetFormatsAsync()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (_data is not null)
|
||||
{
|
||||
return _data.GetDataFormats().ToArray();
|
||||
}
|
||||
|
||||
if (_text is not null)
|
||||
{
|
||||
return new[] { DataFormats.Text };
|
||||
}
|
||||
|
||||
return Array.Empty<string>();
|
||||
});
|
||||
return Task.FromResult<string[]>(_data?.GetDataFormats().ToArray() ?? []);
|
||||
}
|
||||
|
||||
public async Task<object?> GetDataAsync(string format)
|
||||
public Task<object?> GetDataAsync(string format)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
if (format == DataFormats.Text)
|
||||
return _text;
|
||||
if (format == DataFormats.Files && _data is not null)
|
||||
return _data.GetFiles();
|
||||
if (format == DataFormats.FileNames && _data is not null)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
return _data.GetFileNames();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
else
|
||||
return (object?)_data;
|
||||
});
|
||||
return Task.FromResult(_data?.Get(format));
|
||||
}
|
||||
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult(_data);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
|
||||
@@ -73,6 +73,8 @@ internal class NuiClipboardImpl : IClipboard
|
||||
|
||||
public Task<string[]> GetFormatsAsync() =>
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Avalonia.Win32
|
||||
{
|
||||
private const int OleRetryCount = 10;
|
||||
private const int OleRetryDelay = 100;
|
||||
|
||||
private DataObject? _lastStoredDataObject;
|
||||
// We can't currently rely on GetNativeIntPtr due to a bug in MicroCom 0.11, so we store the raw CCW reference instead
|
||||
private IntPtr _lastStoredDataObjectIntPtr;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time in milliseconds to sleep before flushing the clipboard after a set.
|
||||
/// </summary>
|
||||
@@ -93,7 +98,18 @@ namespace Avalonia.Win32
|
||||
var hr = UnmanagedMethods.OleSetClipboard(ptr);
|
||||
|
||||
if (hr == 0)
|
||||
{
|
||||
_lastStoredDataObject = wrapper;
|
||||
// TODO: Replace with GetNativeIntPtr in TryGetInProcessDataObjectAsync
|
||||
// once MicroCom is fixed
|
||||
_lastStoredDataObjectIntPtr = ptr;
|
||||
wrapper.OnDestroyed += delegate
|
||||
{
|
||||
if (_lastStoredDataObjectIntPtr == ptr)
|
||||
_lastStoredDataObjectIntPtr = IntPtr.Zero;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (--i == 0)
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
@@ -150,6 +166,16 @@ namespace Avalonia.Win32
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync()
|
||||
{
|
||||
if (_lastStoredDataObject?.IsDisposed != false
|
||||
|| _lastStoredDataObjectIntPtr == IntPtr.Zero
|
||||
|| UnmanagedMethods.OleIsCurrentClipboard(_lastStoredDataObjectIntPtr) != 0)
|
||||
return Task.FromResult<IDataObject?>(null);
|
||||
|
||||
return Task.FromResult<IDataObject?>(_lastStoredDataObject.Wrapped);
|
||||
}
|
||||
/// <summary>
|
||||
/// Permanently renders the contents of the last IDataObject that was set onto the clipboard.
|
||||
/// </summary>
|
||||
|
||||
@@ -104,6 +104,8 @@ namespace Avalonia.Win32
|
||||
|
||||
private IDataObject _wrapped;
|
||||
public IDataObject Wrapped => _wrapped;
|
||||
public bool IsDisposed => _wrapped == null!;
|
||||
public event Action? OnDestroyed;
|
||||
|
||||
public DataObject(IDataObject wrapped)
|
||||
{
|
||||
@@ -375,6 +377,7 @@ namespace Avalonia.Win32
|
||||
|
||||
protected override void Destroyed()
|
||||
{
|
||||
OnDestroyed?.Invoke();
|
||||
ReleaseWrapped();
|
||||
}
|
||||
|
||||
|
||||
@@ -1578,6 +1578,9 @@ namespace Avalonia.Win32.Interop
|
||||
|
||||
[DllImport("ole32.dll", PreserveSig = true)]
|
||||
public static extern int OleSetClipboard(IntPtr dataObject);
|
||||
|
||||
[DllImport("ole32.dll", PreserveSig = true)]
|
||||
public static extern int OleIsCurrentClipboard(IntPtr dataObject);
|
||||
|
||||
[DllImport("kernel32.dll", ExactSpelling = true)]
|
||||
public static extern IntPtr GlobalLock(IntPtr handle);
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace Avalonia.iOS
|
||||
|
||||
return Task.FromResult<object?>(null);
|
||||
}
|
||||
|
||||
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FlushAsync() =>
|
||||
|
||||
@@ -1002,34 +1002,6 @@ namespace Avalonia.Controls.UnitTests
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClipboardStub : IClipboard // in order to get tests working that use the clipboard
|
||||
{
|
||||
private string _text;
|
||||
|
||||
public Task<string> GetTextAsync() => Task.FromResult(_text);
|
||||
|
||||
public Task SetTextAsync(string text)
|
||||
{
|
||||
_text = text;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ClearAsync()
|
||||
{
|
||||
_text = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
|
||||
|
||||
public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
|
||||
|
||||
public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
|
||||
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
}
|
||||
|
||||
private class TestTopLevel : TopLevel
|
||||
{
|
||||
private readonly ILayoutManager _layoutManager;
|
||||
@@ -1048,7 +1020,7 @@ namespace Avalonia.Controls.UnitTests
|
||||
var clipboard = new Mock<ITopLevelImpl>();
|
||||
clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
|
||||
clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
|
||||
.Returns(new ClipboardStub());
|
||||
.Returns(new HeadlessClipboardStub());
|
||||
clipboard.SetupGet(x => x.RenderScaling).Returns(1);
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
@@ -2147,34 +2147,6 @@ namespace Avalonia.Controls.UnitTests
|
||||
}
|
||||
}
|
||||
|
||||
private class ClipboardStub : IClipboard // in order to get tests working that use the clipboard
|
||||
{
|
||||
private string? _text;
|
||||
|
||||
public Task<string?> GetTextAsync() => Task.FromResult(_text);
|
||||
|
||||
public Task SetTextAsync(string? text)
|
||||
{
|
||||
_text = text;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ClearAsync()
|
||||
{
|
||||
_text = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
|
||||
|
||||
public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
|
||||
|
||||
public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
|
||||
|
||||
public Task FlushAsync() =>
|
||||
Task.CompletedTask;
|
||||
}
|
||||
|
||||
private class TestTopLevel : TopLevel
|
||||
{
|
||||
private readonly ILayoutManager _layoutManager;
|
||||
@@ -2193,7 +2165,7 @@ namespace Avalonia.Controls.UnitTests
|
||||
var clipboard = new Mock<ITopLevelImpl>();
|
||||
clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
|
||||
clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
|
||||
.Returns(new ClipboardStub());
|
||||
.Returns(new HeadlessClipboardStub());
|
||||
clipboard.SetupGet(x => x.RenderScaling).Returns(1);
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user