Browse Source

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>
pull/18430/head
Nikita Tsukanov 2 years ago
committed by GitHub
parent
commit
c3b4d61b8a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      api/Avalonia.nupkg.xml
  2. 18
      native/Avalonia.Native/src/OSX/clipboard.mm
  3. 3
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  4. 46
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  5. 2
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  6. 9
      src/Avalonia.Base/Input/Platform/IClipboard.cs
  7. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  8. 26
      src/Avalonia.Native/ClipboardImpl.cs
  9. 4
      src/Avalonia.Native/avn.idl
  10. 7
      src/Avalonia.X11/X11Clipboard.cs
  11. 2
      src/Browser/Avalonia.Browser/ClipboardImpl.cs
  12. 51
      src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs
  13. 2
      src/Tizen/Avalonia.Tizen/NuiClipboardImpl.cs
  14. 26
      src/Windows/Avalonia.Win32/ClipboardImpl.cs
  15. 3
      src/Windows/Avalonia.Win32/DataObject.cs
  16. 3
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  17. 2
      src/iOS/Avalonia.iOS/ClipboardImpl.cs
  18. 30
      tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs
  19. 30
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

12
api/Avalonia.nupkg.xml

@ -51,19 +51,25 @@
</Suppression> </Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFileAsync(System.String)</Target> <Target>M:Avalonia.Input.Platform.IClipboard.FlushAsync</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left> <Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right> <Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFolderAsync(System.String)</Target> <Target>M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataObjectAsync</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left> <Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right> <Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.FlushAsync</Target> <Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFileAsync(System.String)</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.GetFolderAsync(System.String)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left> <Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right> <Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>

18
native/Avalonia.Native/src/OSX/clipboard.mm

@ -148,17 +148,20 @@ public:
} }
virtual HRESULT Clear() override virtual HRESULT Clear(int64_t* rv) override
{ {
START_COM_CALL; START_COM_CALL;
@autoreleasepool @autoreleasepool
{ {
if(_item != nil) if(_item != nil)
{
_item = [NSPasteboardItem new]; _item = [NSPasteboardItem new];
return 0;
}
else else
{ {
[_pb clearContents]; *rv = [_pb clearContents];
[_pb setString:@"" forType:NSPasteboardTypeString]; [_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 virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override
{ {
START_COM_CALL; START_COM_CALL;

3
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -15,6 +15,9 @@
<Button Click="GetFormats" Content="Get clipboard formats" /> <Button Click="GetFormats" Content="Get clipboard formats" />
<Button Click="Clear" Content="Clear clipboard" /> <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" <TextBox x:Name="ClipboardContent"
MinHeight="100" MinHeight="100"
AcceptsReturn="True" AcceptsReturn="True"

46
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -3,13 +3,17 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO; using Avalonia.Platform.Storage.FileIO;
using Avalonia.Threading;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -18,8 +22,13 @@ namespace ControlCatalog.Pages
private INotificationManager? _notificationManager; private INotificationManager? _notificationManager;
private INotificationManager NotificationManager => _notificationManager private INotificationManager NotificationManager => _notificationManager
??= new WindowNotificationManager(TopLevel.GetTopLevel(this)!); ??= new WindowNotificationManager(TopLevel.GetTopLevel(this)!);
private readonly DispatcherTimer _clipboardLastDataObjectChecker;
private DataObject? _storedDataObject;
public ClipboardPage() public ClipboardPage()
{ {
_clipboardLastDataObjectChecker =
new DispatcherTimer(TimeSpan.FromSeconds(0.5), default, CheckLastDataObject);
InitializeComponent(); InitializeComponent();
} }
@ -48,7 +57,7 @@ namespace ControlCatalog.Pages
{ {
if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard) if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{ {
var dataObject = new DataObject(); var dataObject = _storedDataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty); dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await clipboard.SetDataObjectAsync(dataObject); await clipboard.SetDataObjectAsync(dataObject);
} }
@ -96,7 +105,7 @@ namespace ControlCatalog.Pages
if (files.Count > 0) if (files.Count > 0)
{ {
var dataObject = new DataObject(); var dataObject = _storedDataObject = new DataObject();
dataObject.Set(DataFormats.Files, files); dataObject.Set(DataFormats.Files, files);
await clipboard.SetDataObjectAsync(dataObject); await clipboard.SetDataObjectAsync(dataObject);
NotificationManager.Show(new Notification("Success", "Copy completated.", NotificationType.Success)); 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;
}
}
} }
} }

2
src/Android/Avalonia.Android/Platform/ClipboardImpl.cs

@ -56,6 +56,8 @@ namespace Avalonia.Android.Platform
public Task<object?> GetDataAsync(string format) => throw new PlatformNotSupportedException(); public Task<object?> GetDataAsync(string format) => throw new PlatformNotSupportedException();
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>
Task.CompletedTask; Task.CompletedTask;

9
src/Avalonia.Base/Input/Platform/IClipboard.cs

@ -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> /// <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> /// <returns></returns>
Task<object?> GetDataAsync(string format); 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();
} }
} }

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -226,6 +226,8 @@ namespace Avalonia.DesignerSupport.Remote
public Task<object> GetDataAsync(string format) => Task.FromResult((object)null); public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
public Task<IDataObject> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject>(null);
public Task FlushAsync() => public Task FlushAsync() =>
Task.CompletedTask; Task.CompletedTask;
} }

26
src/Avalonia.Native/ClipboardImpl.cs

@ -17,6 +17,8 @@ namespace Avalonia.Native
class ClipboardImpl : IClipboard, IDisposable class ClipboardImpl : IClipboard, IDisposable
{ {
private IAvnClipboard? _native; private IAvnClipboard? _native;
private IDataObject? _savedDataObject;
private long _lastClearChangeCount;
// TODO hide native types behind IAvnClipboard abstraction, so managed side won't depend on macOS. // TODO hide native types behind IAvnClipboard abstraction, so managed side won't depend on macOS.
private const string NSPasteboardTypeString = "public.utf8-plain-text"; private const string NSPasteboardTypeString = "public.utf8-plain-text";
@ -30,10 +32,15 @@ namespace Avalonia.Native
private IAvnClipboard Native private IAvnClipboard Native
=> _native ?? throw new ObjectDisposedException(nameof(ClipboardImpl)); => _native ?? throw new ObjectDisposedException(nameof(ClipboardImpl));
private void ClearCore()
{
_savedDataObject = null;
_lastClearChangeCount = Native.Clear();
}
public Task ClearAsync() public Task ClearAsync()
{ {
Native.Clear(); ClearCore();
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -47,7 +54,7 @@ namespace Avalonia.Native
{ {
var native = Native; var native = Native;
native.Clear(); ClearCore();
if (text != null) if (text != null)
native.SetText(NSPasteboardTypeString, text); native.SetText(NSPasteboardTypeString, text);
@ -83,6 +90,7 @@ namespace Avalonia.Native
public void Dispose() public void Dispose()
{ {
_savedDataObject = null;
_native?.Dispose(); _native?.Dispose();
_native = null; _native = null;
} }
@ -111,7 +119,7 @@ namespace Avalonia.Native
public unsafe Task SetDataObjectAsync(IDataObject data) 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. // If there is multiple values with the same "to" format, prefer these that were not mapped.
var formats = data.GetDataFormats().Select(f => var formats = data.GetDataFormats().Select(f =>
@ -163,6 +171,8 @@ namespace Avalonia.Native
break; break;
} }
} }
_savedDataObject = data;
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -183,9 +193,17 @@ namespace Avalonia.Native
return n.Bytes; return n.Bytes;
} }
public Task<IDataObject?> TryGetInProcessDataObjectAsync()
{
if (Native.ChangeCount != _lastClearChangeCount)
_savedDataObject = null;
return Task.FromResult(_savedDataObject);
}
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>
Task.CompletedTask; Task.CompletedTask;
} }
class ClipboardDataObject : IDataObject, IDisposable class ClipboardDataObject : IDataObject, IDisposable

4
src/Avalonia.Native/avn.idl

@ -2,6 +2,7 @@
@clr-access internal @clr-access internal
@clr-map bool int @clr-map bool int
@clr-map u_int64_t ulong @clr-map u_int64_t ulong
@clr-map int64_t long
@clr-map long IntPtr @clr-map long IntPtr
@cpp-preamble @@ @cpp-preamble @@
#pragma once #pragma once
@ -972,7 +973,8 @@ interface IAvnClipboard : IUnknown
HRESULT SetBytes(char* type, void* utf8Text, int len); HRESULT SetBytes(char* type, void* utf8Text, int len);
HRESULT GetBytes(char* type, IAvnString**ppv); HRESULT GetBytes(char* type, IAvnString**ppv);
HRESULT Clear(); HRESULT Clear(int64_t* ret);
HRESULT GetChangeCount(int64_t* ret);
} }
[uuid(3f998545-f027-4d4d-bd2a-1a80926d984e)] [uuid(3f998545-f027-4d4d-bd2a-1a80926d984e)]

7
src/Avalonia.X11/X11Clipboard.cs

@ -314,6 +314,13 @@ namespace Avalonia.X11
return StoreAtomsInClipboardManager(data); 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() public async Task<string[]> GetFormatsAsync()
{ {
if (!HasOwner) if (!HasOwner)

2
src/Browser/Avalonia.Browser/ClipboardImpl.cs

@ -25,6 +25,8 @@ namespace Avalonia.Browser
public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>()); public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
public Task<object?> GetDataAsync(string format) => Task.FromResult<object?>(null); public Task<object?> GetDataAsync(string format) => Task.FromResult<object?>(null);
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>

51
src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -17,66 +17,47 @@ namespace Avalonia.Headless
{ {
internal class HeadlessClipboardStub : IClipboard internal class HeadlessClipboardStub : IClipboard
{ {
private string? _text;
private IDataObject? _data; private IDataObject? _data;
public Task<string?> GetTextAsync() public Task<string?> GetTextAsync()
{ {
return Task.Run(() => _text); return Task.FromResult(_data?.GetText());
} }
public Task SetTextAsync(string? text) 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() public Task ClearAsync()
{ {
return Task.Run(() => _text = null); _data = null;
return Task.CompletedTask;
} }
public Task SetDataObjectAsync(IDataObject data) public Task SetDataObjectAsync(IDataObject data)
{ {
return Task.Run(() => _data = data); _data = data;
return Task.CompletedTask;
} }
public Task<string[]> GetFormatsAsync() public Task<string[]> GetFormatsAsync()
{ {
return Task.Run(() => return Task.FromResult<string[]>(_data?.GetDataFormats().ToArray() ?? []);
{
if (_data is not null)
{
return _data.GetDataFormats().ToArray();
}
if (_text is not null)
{
return new[] { DataFormats.Text };
}
return Array.Empty<string>();
});
} }
public async Task<object?> GetDataAsync(string format) public Task<object?> GetDataAsync(string format)
{ {
return await Task.Run(() => return Task.FromResult(_data?.Get(format));
{
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;
});
} }
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult(_data);
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>
Task.CompletedTask; Task.CompletedTask;

2
src/Tizen/Avalonia.Tizen/NuiClipboardImpl.cs

@ -73,6 +73,8 @@ internal class NuiClipboardImpl : IClipboard
public Task<string[]> GetFormatsAsync() => public Task<string[]> GetFormatsAsync() =>
throw new PlatformNotSupportedException(); throw new PlatformNotSupportedException();
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>

26
src/Windows/Avalonia.Win32/ClipboardImpl.cs

@ -15,6 +15,11 @@ namespace Avalonia.Win32
{ {
private const int OleRetryCount = 10; private const int OleRetryCount = 10;
private const int OleRetryDelay = 100; 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> /// <summary>
/// The amount of time in milliseconds to sleep before flushing the clipboard after a set. /// The amount of time in milliseconds to sleep before flushing the clipboard after a set.
/// </summary> /// </summary>
@ -93,7 +98,18 @@ namespace Avalonia.Win32
var hr = UnmanagedMethods.OleSetClipboard(ptr); var hr = UnmanagedMethods.OleSetClipboard(ptr);
if (hr == 0) 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; break;
}
if (--i == 0) if (--i == 0)
Marshal.ThrowExceptionForHR(hr); 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> /// <summary>
/// Permanently renders the contents of the last IDataObject that was set onto the clipboard. /// Permanently renders the contents of the last IDataObject that was set onto the clipboard.
/// </summary> /// </summary>

3
src/Windows/Avalonia.Win32/DataObject.cs

@ -104,6 +104,8 @@ namespace Avalonia.Win32
private IDataObject _wrapped; private IDataObject _wrapped;
public IDataObject Wrapped => _wrapped; public IDataObject Wrapped => _wrapped;
public bool IsDisposed => _wrapped == null!;
public event Action? OnDestroyed;
public DataObject(IDataObject wrapped) public DataObject(IDataObject wrapped)
{ {
@ -375,6 +377,7 @@ namespace Avalonia.Win32
protected override void Destroyed() protected override void Destroyed()
{ {
OnDestroyed?.Invoke();
ReleaseWrapped(); ReleaseWrapped();
} }

3
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1578,6 +1578,9 @@ namespace Avalonia.Win32.Interop
[DllImport("ole32.dll", PreserveSig = true)] [DllImport("ole32.dll", PreserveSig = true)]
public static extern int OleSetClipboard(IntPtr dataObject); public static extern int OleSetClipboard(IntPtr dataObject);
[DllImport("ole32.dll", PreserveSig = true)]
public static extern int OleIsCurrentClipboard(IntPtr dataObject);
[DllImport("kernel32.dll", ExactSpelling = true)] [DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle); public static extern IntPtr GlobalLock(IntPtr handle);

2
src/iOS/Avalonia.iOS/ClipboardImpl.cs

@ -57,6 +57,8 @@ namespace Avalonia.iOS
return Task.FromResult<object?>(null); return Task.FromResult<object?>(null);
} }
public Task<IDataObject?> TryGetInProcessDataObjectAsync() => Task.FromResult<IDataObject?>(null);
/// <inheritdoc /> /// <inheritdoc />
public Task FlushAsync() => public Task FlushAsync() =>

30
tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs

@ -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 class TestTopLevel : TopLevel
{ {
private readonly ILayoutManager _layoutManager; private readonly ILayoutManager _layoutManager;
@ -1048,7 +1020,7 @@ namespace Avalonia.Controls.UnitTests
var clipboard = new Mock<ITopLevelImpl>(); var clipboard = new Mock<ITopLevelImpl>();
clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor()); clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard))) clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
.Returns(new ClipboardStub()); .Returns(new HeadlessClipboardStub());
clipboard.SetupGet(x => x.RenderScaling).Returns(1); clipboard.SetupGet(x => x.RenderScaling).Returns(1);
return clipboard; return clipboard;
} }

30
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -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 class TestTopLevel : TopLevel
{ {
private readonly ILayoutManager _layoutManager; private readonly ILayoutManager _layoutManager;
@ -2193,7 +2165,7 @@ namespace Avalonia.Controls.UnitTests
var clipboard = new Mock<ITopLevelImpl>(); var clipboard = new Mock<ITopLevelImpl>();
clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor()); clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard))) clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
.Returns(new ClipboardStub()); .Returns(new HeadlessClipboardStub());
clipboard.SetupGet(x => x.RenderScaling).Returns(1); clipboard.SetupGet(x => x.RenderScaling).Returns(1);
return clipboard; return clipboard;
} }

Loading…
Cancel
Save