Browse Source

Warning cleanup 2 (#13696)

pull/13708/head
Julien Lebosquain 2 years ago
committed by GitHub
parent
commit
3fa13d3b64
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build/XUnit.props
  2. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  3. 45
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  4. 2
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  5. 7
      samples/Generators.Sandbox/Controls/CustomTextBox.cs
  6. 50
      samples/GpuInterop/D3DDemo/D3D11DemoControl.cs
  7. 9
      samples/GpuInterop/DrawingSurfaceDemoBase.cs
  8. 12
      samples/GpuInterop/GpuDemo.axaml.cs
  9. 4
      samples/GpuInterop/VulkanDemo/VulkanCommandBufferPool.cs
  10. 14
      samples/GpuInterop/VulkanDemo/VulkanContent.cs
  11. 38
      samples/GpuInterop/VulkanDemo/VulkanContext.cs
  12. 30
      samples/GpuInterop/VulkanDemo/VulkanImage.cs
  13. 2
      samples/GpuInterop/VulkanDemo/VulkanSwapchain.cs
  14. 37
      samples/IntegrationTestApp/MainWindow.axaml.cs
  15. 12
      src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs
  16. 15
      src/Android/Avalonia.Android/AvaloniaMainActivity.cs
  17. 2
      src/Android/Avalonia.Android/AvaloniaView.cs
  18. 37
      src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
  19. 8
      src/Android/Avalonia.Android/Platform/AndroidSystemNavigationManager.cs
  20. 22
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  21. 4
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  22. 8
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  23. 45
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  24. 4
      src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs
  25. 2
      src/Tizen/Avalonia.Tizen/Avalonia.Tizen.csproj
  26. 43
      src/Tizen/Avalonia.Tizen/NuiAvaloniaView.cs
  27. 2
      src/Tizen/Avalonia.Tizen/NuiAvaloniaViewTextEditable.cs
  28. 4
      src/Tizen/Avalonia.Tizen/NuiGlPlatform.cs
  29. 13
      src/Tizen/Avalonia.Tizen/NuiTizenApplication.cs
  30. 34
      src/Tizen/Avalonia.Tizen/Platform/Permissions.cs
  31. 2
      src/Tizen/Avalonia.Tizen/Stubs.cs
  32. 22
      src/Tizen/Avalonia.Tizen/TizenPlatform.cs
  33. 10
      src/Tizen/Avalonia.Tizen/TizenThreadingInterface.cs
  34. 2
      src/Tizen/Avalonia.Tizen/TopLevelImpl.cs
  35. 70
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  36. 2
      src/iOS/Avalonia.iOS/Storage/IOSSecurityScopedStream.cs
  37. 15
      src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs
  38. 13
      src/iOS/Avalonia.iOS/TextInputResponder.cs
  39. 11
      tests/Avalonia.Base.UnitTests/DispatcherTests.cs
  40. 13
      tests/Avalonia.Base.UnitTests/Media/EffectTests.cs
  41. 6
      tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
  42. 53
      tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
  43. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  44. 3
      tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
  45. 12
      tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
  46. 224
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  47. 19
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
  48. 14
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  49. 2
      tests/Avalonia.RenderTests/Media/TileBrushTests.cs
  50. 4
      tests/Avalonia.RenderTests/TestBase.cs
  51. 89
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  52. 78
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

2
build/XUnit.props

@ -6,7 +6,7 @@
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" /> <PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" /> <PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
<PackageReference Include="xunit.runner.console" Version="2.4.2" /> <PackageReference Include="xunit.runner.console" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" /> <PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
</ItemGroup> </ItemGroup>

2
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -160,7 +160,7 @@ namespace ControlCatalog.Pages
} }
else else
{ {
SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result)); SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result!));
results.ItemsSource = new[] { result }; results.ItemsSource = new[] { result };
resultsVisible.IsVisible = true; resultsVisible.IsVisible = true;
} }

45
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
@ -14,26 +15,54 @@ namespace ControlCatalog.Pages
private const string CustomFormat = "application/xxx-avalonia-controlcatalog-custom"; private const string CustomFormat = "application/xxx-avalonia-controlcatalog-custom";
public DragAndDropPage() public DragAndDropPage()
{ {
this.InitializeComponent(); InitializeComponent();
_dropState = this.Get<TextBlock>("DropState"); _dropState = this.Get<TextBlock>("DropState");
int textCount = 0; int textCount = 0;
SetupDnd("Text", d => d.Set(DataFormats.Text,
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move); SetupDnd(
SetupDnd("Files", async d => d.Set(DataFormats.Files, new[] { await (VisualRoot as TopLevel)!.StorageProvider.TryGetFileFromPathAsync(Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName) }), DragDropEffects.Copy); "Text",
d => d.Set(DataFormats.Text, $"Text was dragged {++textCount} times"),
DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd(
"Custom",
d => d.Set(CustomFormat, "Test123"),
DragDropEffects.Move);
SetupDnd(
"Files",
async d =>
{
if (Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName is { } name &&
TopLevel.GetTopLevel(this) is { } topLevel &&
await topLevel.StorageProvider.TryGetFileFromPathAsync(name) is { } storageFile)
{
d.Set(DataFormats.Files, new[] { storageFile });
}
},
DragDropEffects.Copy);
} }
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) private void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) =>
SetupDnd(
suffix,
o =>
{
factory(o);
return Task.CompletedTask;
},
effects);
private void SetupDnd(string suffix, Func<DataObject, Task> factory, DragDropEffects effects)
{ {
var dragMe = this.Get<Border>("DragMe" + suffix); var dragMe = this.Get<Border>("DragMe" + suffix);
var dragState = this.Get<TextBlock>("DragState" + suffix); var dragState = this.Get<TextBlock>("DragState" + suffix);
async void DoDrag(object? sender, Avalonia.Input.PointerPressedEventArgs e) async void DoDrag(object? sender, PointerPressedEventArgs e)
{ {
var dragData = new DataObject(); var dragData = new DataObject();
factory(dragData); await factory(dragData);
var result = await DragDrop.DoDragDrop(e, dragData, effects); var result = await DragDrop.DoDragDrop(e, dragData, effects);
switch (result) switch (result)

2
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@ -48,7 +48,7 @@ namespace ControlCatalog.ViewModels
foreach (var item in items) foreach (var item in items)
{ {
Items.Remove(item); Items.Remove(item!);
} }
}); });

7
samples/Generators.Sandbox/Controls/CustomTextBox.cs

@ -1,10 +1,9 @@
using System; using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling;
namespace Generators.Sandbox.Controls; namespace Generators.Sandbox.Controls;
public class CustomTextBox : TextBox, IStyleable public class CustomTextBox : TextBox
{ {
Type IStyleable.StyleKey => typeof(TextBox); protected override Type StyleKeyOverride => typeof(TextBox);
} }

50
samples/GpuInterop/D3DDemo/D3D11DemoControl.cs

@ -20,21 +20,21 @@ namespace GpuInterop.D3DDemo;
public class D3D11DemoControl : DrawingSurfaceDemoBase public class D3D11DemoControl : DrawingSurfaceDemoBase
{ {
private D3DDevice _device; private D3DDevice? _device;
private D3D11Swapchain _swapchain; private D3D11Swapchain? _swapchain;
private SharpDX.Direct3D11.DeviceContext _context; private DeviceContext? _context;
private Matrix _view; private Matrix _view;
private PixelSize _lastSize; private PixelSize _lastSize;
private Texture2D _depthBuffer; private Texture2D? _depthBuffer;
private DepthStencilView _depthView; private DepthStencilView? _depthView;
private Matrix _proj; private Matrix _proj;
private Buffer _constantBuffer; private Buffer? _constantBuffer;
private Stopwatch _st = Stopwatch.StartNew(); private readonly Stopwatch _st = Stopwatch.StartNew();
protected override (bool success, string info) InitializeGraphicsResources(Compositor compositor, protected override (bool success, string info) InitializeGraphicsResources(Compositor compositor,
CompositionDrawingSurface surface, ICompositionGpuInterop interop) CompositionDrawingSurface surface, ICompositionGpuInterop interop)
{ {
if (interop?.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes if (interop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
.D3D11TextureGlobalSharedHandle) != true) .D3D11TextureGlobalSharedHandle) != true)
return (false, "DXGI shared handle import is not supported by the current graphics backend"); return (false, "DXGI shared handle import is not supported by the current graphics backend");
@ -60,8 +60,12 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
protected override void FreeGraphicsResources() protected override void FreeGraphicsResources()
{ {
_swapchain.DisposeAsync(); if (_swapchain is not null)
_swapchain = null!; {
_swapchain.DisposeAsync().GetAwaiter().GetResult();
_swapchain = null;
}
Utilities.Dispose(ref _depthView); Utilities.Dispose(ref _depthView);
Utilities.Dispose(ref _depthBuffer); Utilities.Dispose(ref _depthBuffer);
Utilities.Dispose(ref _constantBuffer); Utilities.Dispose(ref _constantBuffer);
@ -80,10 +84,10 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
_lastSize = pixelSize; _lastSize = pixelSize;
Resize(pixelSize); Resize(pixelSize);
} }
using (_swapchain.BeginDraw(pixelSize, out var renderView)) using (_swapchain!.BeginDraw(pixelSize, out var renderView))
{ {
_device.ImmediateContext.OutputMerger.SetTargets(_depthView, renderView); _device!.ImmediateContext.OutputMerger.SetTargets(_depthView, renderView);
var viewProj = Matrix.Multiply(_view, _proj); var viewProj = Matrix.Multiply(_view, _proj);
var context = _device.ImmediateContext; var context = _device.ImmediateContext;
@ -101,10 +105,10 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
var ypr = Matrix4x4.CreateFromYawPitchRoll(Yaw, Pitch, Roll); var ypr = Matrix4x4.CreateFromYawPitchRoll(Yaw, Pitch, Roll);
// Update WorldViewProj Matrix // Update WorldViewProj Matrix
var worldViewProj = Matrix.RotationX((float)Yaw) * Matrix.RotationY((float)Pitch) var worldViewProj = Matrix.RotationX(Yaw) * Matrix.RotationY(Pitch)
* Matrix.RotationZ((float)Roll) * Matrix.RotationZ(Roll)
* Matrix.Scaling(new Vector3(scaleX, scaleY, 1)) * Matrix.Scaling(new Vector3(scaleX, scaleY, 1))
* viewProj; * viewProj;
worldViewProj.Transpose(); worldViewProj.Transpose();
context.UpdateSubresource(ref worldViewProj, _constantBuffer); context.UpdateSubresource(ref worldViewProj, _constantBuffer);
@ -112,21 +116,25 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
context.Draw(36, 0); context.Draw(36, 0);
_context.Flush(); _context!.Flush();
} }
} }
private void Resize(PixelSize size) private void Resize(PixelSize size)
{ {
Utilities.Dispose(ref _depthBuffer); Utilities.Dispose(ref _depthBuffer);
if (_device is null)
return;
_depthBuffer = new Texture2D(_device, _depthBuffer = new Texture2D(_device,
new Texture2DDescription() new Texture2DDescription()
{ {
Format = Format.D32_Float_S8X24_UInt, Format = Format.D32_Float_S8X24_UInt,
ArraySize = 1, ArraySize = 1,
MipLevels = 1, MipLevels = 1,
Width = (int)size.Width, Width = size.Width,
Height = (int)size.Height, Height = size.Height,
SampleDescription = new SampleDescription(1, 0), SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default, Usage = ResourceUsage.Default,
BindFlags = BindFlags.DepthStencil, BindFlags = BindFlags.DepthStencil,
@ -138,9 +146,9 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase
_depthView = new DepthStencilView(_device, _depthBuffer); _depthView = new DepthStencilView(_device, _depthBuffer);
// Setup targets and viewport for rendering // Setup targets and viewport for rendering
_device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); _device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, size.Width, size.Height, 0.0f, 1.0f));
// Setup new projection matrix with correct aspect ratio // Setup new projection matrix with correct aspect ratio
_proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, size.Width / (float) size.Height, 0.1f, 100.0f);
} }
} }

9
samples/GpuInterop/DrawingSurfaceDemoBase.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
@ -13,12 +12,12 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
{ {
private CompositionSurfaceVisual? _visual; private CompositionSurfaceVisual? _visual;
private Compositor? _compositor; private Compositor? _compositor;
private Action _update; private readonly Action _update;
private string _info; private string _info = string.Empty;
private bool _updateQueued; private bool _updateQueued;
private bool _initialized; private bool _initialized;
protected CompositionDrawingSurface Surface { get; private set; } protected CompositionDrawingSurface? Surface { get; private set; }
public DrawingSurfaceDemoBase() public DrawingSurfaceDemoBase()
{ {
@ -113,7 +112,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
protected abstract void RenderFrame(PixelSize pixelSize); protected abstract void RenderFrame(PixelSize pixelSize);
protected virtual bool SupportsDisco => false; protected virtual bool SupportsDisco => false;
public void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco) public void Update(GpuDemo? parent, float yaw, float pitch, float roll, float disco)
{ {
ParentControl = parent; ParentControl = parent;
if (ParentControl != null) if (ParentControl != null)

12
samples/GpuInterop/GpuDemo.axaml.cs

@ -80,13 +80,13 @@ public class GpuDemo : UserControl
set => SetAndRaise(DiscoVisibleProperty, ref _discoVisible, value); set => SetAndRaise(DiscoVisibleProperty, ref _discoVisible, value);
} }
private IGpuDemo _demo; private IGpuDemo? _demo;
public static readonly DirectProperty<GpuDemo, IGpuDemo> DemoProperty = public static readonly DirectProperty<GpuDemo, IGpuDemo?> DemoProperty =
AvaloniaProperty.RegisterDirect<GpuDemo, IGpuDemo>("Demo", o => o.Demo, AvaloniaProperty.RegisterDirect<GpuDemo, IGpuDemo?>("Demo", o => o.Demo,
(o, v) => o._demo = v); (o, v) => o._demo = v);
public IGpuDemo Demo public IGpuDemo? Demo
{ {
get => _demo; get => _demo;
set => SetAndRaise(DemoProperty, ref _demo, value); set => SetAndRaise(DemoProperty, ref _demo, value);
@ -102,7 +102,7 @@ public class GpuDemo : UserControl
) )
{ {
if (change.Property == DemoProperty) if (change.Property == DemoProperty)
((IGpuDemo)change.OldValue)?.Update(null, 0, 0, 0, 0); ((IGpuDemo?)change.OldValue)?.Update(null, 0, 0, 0, 0);
_demo?.Update(this, Yaw, Pitch, Roll, Disco); _demo?.Update(this, Yaw, Pitch, Roll, Disco);
} }
@ -112,5 +112,5 @@ public class GpuDemo : UserControl
public interface IGpuDemo public interface IGpuDemo
{ {
void Update(GpuDemo parent, float yaw, float pitch, float roll, float disco); void Update(GpuDemo? parent, float yaw, float pitch, float roll, float disco);
} }

4
samples/GpuInterop/VulkanDemo/VulkanCommandBufferPool.cs

@ -13,7 +13,7 @@ namespace Avalonia.Vulkan
private readonly CommandPool _commandPool; private readonly CommandPool _commandPool;
private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new(); private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new();
private object _lock = new object(); private readonly object _lock = new();
public unsafe VulkanCommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex) public unsafe VulkanCommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex)
{ {
@ -167,7 +167,7 @@ namespace Avalonia.Vulkan
ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default, ReadOnlySpan<PipelineStageFlags> waitDstStageMask = default,
ReadOnlySpan<Semaphore> signalSemaphores = default, ReadOnlySpan<Semaphore> signalSemaphores = default,
Fence? fence = null, Fence? fence = null,
KeyedMutexSubmitInfo keyedMutex = null) KeyedMutexSubmitInfo? keyedMutex = null)
{ {
EndRecording(); EndRecording();

14
samples/GpuInterop/VulkanDemo/VulkanContent.cs

@ -39,7 +39,7 @@ unsafe class VulkanContent : IDisposable
{ {
_context = context; _context = context;
var name = typeof(VulkanContent).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin")); var name = typeof(VulkanContent).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
using (var sr = new BinaryReader(typeof(VulkanContent).Assembly.GetManifestResourceStream(name))) using (var sr = new BinaryReader(typeof(VulkanContent).Assembly.GetManifestResourceStream(name)!))
{ {
var buf = new byte[sr.ReadInt32()]; var buf = new byte[sr.ReadInt32()];
sr.Read(buf, 0, buf.Length); sr.Read(buf, 0, buf.Length);
@ -115,7 +115,7 @@ unsafe class VulkanContent : IDisposable
{ {
var name = typeof(VulkanContent).Assembly.GetManifestResourceNames() var name = typeof(VulkanContent).Assembly.GetManifestResourceNames()
.First(x => x.Contains((fragment ? "frag" : "vert") + ".spirv")); .First(x => x.Contains((fragment ? "frag" : "vert") + ".spirv"));
using (var sr = typeof(VulkanContent).Assembly.GetManifestResourceStream(name)) using (var sr = typeof(VulkanContent).Assembly.GetManifestResourceStream(name)!)
{ {
using (var mem = new MemoryStream()) using (var mem = new MemoryStream())
{ {
@ -158,7 +158,7 @@ unsafe class VulkanContent : IDisposable
var commandBuffer = _context.Pool.CreateCommandBuffer(); var commandBuffer = _context.Pool.CreateCommandBuffer();
commandBuffer.BeginRecording(); commandBuffer.BeginRecording();
_colorAttachment.TransitionLayout(commandBuffer.InternalHandle, _colorAttachment!.TransitionLayout(commandBuffer.InternalHandle,
ImageLayout.Undefined, AccessFlags.None, ImageLayout.Undefined, AccessFlags.None,
ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentWriteBit); ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentWriteBit);
@ -251,9 +251,9 @@ unsafe class VulkanContent : IDisposable
} }
}; };
api.CmdBlitImage(commandBuffer.InternalHandle, _colorAttachment.InternalHandle.Value, api.CmdBlitImage(commandBuffer.InternalHandle, _colorAttachment.InternalHandle,
ImageLayout.TransferSrcOptimal, ImageLayout.TransferSrcOptimal,
image.InternalHandle.Value, ImageLayout.TransferDstOptimal, 1, srcBlitRegion, Filter.Linear); image.InternalHandle, ImageLayout.TransferDstOptimal, 1, srcBlitRegion, Filter.Linear);
commandBuffer.Submit(); commandBuffer.Submit();
} }
@ -393,7 +393,7 @@ unsafe class VulkanContent : IDisposable
var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
var projection = var projection =
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)((float)size.Width / size.Height), Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)size.Width / size.Height,
0.01f, 1000); 0.01f, 1000);
_colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false); _colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false);
@ -808,7 +808,7 @@ unsafe class VulkanContent : IDisposable
static Stopwatch St = Stopwatch.StartNew(); static Stopwatch St = Stopwatch.StartNew();
private bool _isInit; private bool _isInit;
private VulkanImage _colorAttachment; private VulkanImage? _colorAttachment;
private DescriptorSet _descriptorSet; private DescriptorSet _descriptorSet;
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Pack = 4)]

38
samples/GpuInterop/VulkanDemo/VulkanContext.cs

@ -2,34 +2,31 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Vulkan; using Avalonia.Vulkan;
using Silk.NET.Core; using Silk.NET.Core;
using Silk.NET.Core.Native;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.EXT;
using Silk.NET.Vulkan.Extensions.KHR; using Silk.NET.Vulkan.Extensions.KHR;
using SilkNetDemo; using SilkNetDemo;
using SkiaSharp; using SkiaSharp;
using D3DDevice = SharpDX.Direct3D11.Device; using D3DDevice = SharpDX.Direct3D11.Device;
using DxgiDevice = SharpDX.DXGI.Device;
namespace GpuInterop.VulkanDemo; namespace GpuInterop.VulkanDemo;
public unsafe class VulkanContext : IDisposable public unsafe class VulkanContext : IDisposable
{ {
public Vk Api { get; init; } public required Vk Api { get; init; }
public Instance Instance { get; init; } public required Instance Instance { get; init; }
public PhysicalDevice PhysicalDevice { get; init; } public required PhysicalDevice PhysicalDevice { get; init; }
public Device Device { get; init; } public required Device Device { get; init; }
public Queue Queue { get; init; } public required Queue Queue { get; init; }
public uint QueueFamilyIndex { get; init; } public required uint QueueFamilyIndex { get; init; }
public VulkanCommandBufferPool Pool { get; init; } public required VulkanCommandBufferPool Pool { get; init; }
public GRContext GrContext { get; init; } public required GRContext GrContext { get; init; }
public DescriptorPool DescriptorPool { get; init; } public required DescriptorPool DescriptorPool { get; init; }
public D3DDevice? D3DDevice { get; init; } public required D3DDevice? D3DDevice { get; init; }
public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop) public static (VulkanContext? result, string info) TryCreate(ICompositionGpuInterop gpuInterop)
{ {
@ -58,10 +55,8 @@ public unsafe class VulkanContext : IDisposable
enabledExtensions.Add("VK_EXT_debug_utils"); enabledExtensions.Add("VK_EXT_debug_utils");
if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation")) if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
enabledLayers.Add("VK_LAYER_KHRONOS_validation"); enabledLayers.Add("VK_LAYER_KHRONOS_validation");
Instance vkInstance = default;
Silk.NET.Vulkan.PhysicalDevice physicalDevice = default;
Device device = default; Device device = default;
DescriptorPool descriptorPool = default; DescriptorPool descriptorPool = default;
VulkanCommandBufferPool? pool = null; VulkanCommandBufferPool? pool = null;
@ -78,7 +73,7 @@ public unsafe class VulkanContext : IDisposable
EnabledExtensionCount = pRequiredExtensions.UCount, EnabledExtensionCount = pRequiredExtensions.UCount,
PpEnabledLayerNames = pEnabledLayers, PpEnabledLayerNames = pEnabledLayers,
EnabledLayerCount = pEnabledLayers.UCount EnabledLayerCount = pEnabledLayers.UCount
}, null, out vkInstance).ThrowOnError(); }, null, out var vkInstance).ThrowOnError();
if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils)) if (api.TryGetInstanceExtension(vkInstance, out ExtDebugUtils debugUtils))
@ -95,7 +90,7 @@ public unsafe class VulkanContext : IDisposable
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback), PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(LogCallback),
}; };
debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out var messenger); debugUtils.CreateDebugUtilsMessenger(vkInstance, debugCreateInfo, null, out _);
} }
var requireDeviceExtensions = new List<string> var requireDeviceExtensions = new List<string>
@ -158,11 +153,11 @@ public unsafe class VulkanContext : IDisposable
else if (gpuInterop.DeviceUuid != null) else if (gpuInterop.DeviceUuid != null)
{ {
if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16) if (!new Span<byte>(physicalDeviceIDProperties.DeviceUuid, 16)
.SequenceEqual(gpuInterop?.DeviceUuid)) .SequenceEqual(gpuInterop.DeviceUuid))
continue; continue;
} }
physicalDevice = physicalDevices[c]; var physicalDevice = physicalDevices[c];
var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!; var name = Marshal.PtrToStringAnsi(new IntPtr(physicalDeviceProperties2.Properties.DeviceName))!;
@ -251,8 +246,7 @@ public unsafe class VulkanContext : IDisposable
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid( d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8)); new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
var dxgiDevice = d3dDevice?.QueryInterface<DxgiDevice>();
return (new VulkanContext return (new VulkanContext
{ {
Api = api, Api = api,

30
samples/GpuInterop/VulkanDemo/VulkanImage.cs

@ -24,23 +24,23 @@ public unsafe class VulkanImage : IDisposable
private ImageLayout _currentLayout; private ImageLayout _currentLayout;
private AccessFlags _currentAccessFlags; private AccessFlags _currentAccessFlags;
private ImageUsageFlags _imageUsageFlags { get; } private ImageUsageFlags _imageUsageFlags { get; }
private ImageView? _imageView { get; set; } private ImageView _imageView { get; set; }
private DeviceMemory _imageMemory { get; set; } private DeviceMemory _imageMemory { get; set; }
private SharpDX.Direct3D11.Texture2D? _d3dTexture2D; private readonly SharpDX.Direct3D11.Texture2D? _d3dTexture2D;
internal Image? InternalHandle { get; private set; } internal Image InternalHandle { get; private set; }
internal Format Format { get; } internal Format Format { get; }
internal ImageAspectFlags AspectFlags { get; private set; } internal ImageAspectFlags AspectFlags { get; }
public ulong Handle => InternalHandle?.Handle ?? 0; public ulong Handle => InternalHandle.Handle;
public ulong ViewHandle => _imageView?.Handle ?? 0; public ulong ViewHandle => _imageView.Handle;
public uint UsageFlags => (uint) _imageUsageFlags; public uint UsageFlags => (uint) _imageUsageFlags;
public ulong MemoryHandle => _imageMemory.Handle; public ulong MemoryHandle => _imageMemory.Handle;
public DeviceMemory DeviceMemory => _imageMemory; public DeviceMemory DeviceMemory => _imageMemory;
public uint MipLevels { get; private set; } public uint MipLevels { get; }
public Vk Api { get; } public Vk Api { get; }
public PixelSize Size { get; } public PixelSize Size { get; }
public ulong MemorySize { get; private set; } public ulong MemorySize { get; }
public uint CurrentLayout => (uint) _currentLayout; public uint CurrentLayout => (uint) _currentLayout;
public VulkanImage(VulkanContext vk, uint format, PixelSize size, public VulkanImage(VulkanContext vk, uint format, PixelSize size,
@ -93,7 +93,7 @@ public unsafe class VulkanImage : IDisposable
.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError(); .CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
InternalHandle = image; InternalHandle = image;
Api.GetImageMemoryRequirements(_device, InternalHandle.Value, Api.GetImageMemoryRequirements(_device, InternalHandle,
out var memoryRequirements); out var memoryRequirements);
@ -109,7 +109,7 @@ public unsafe class VulkanImage : IDisposable
ImportMemoryWin32HandleInfoKHR handleImport = default; ImportMemoryWin32HandleInfoKHR handleImport = default;
if (exportable && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (exportable && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice, size, Format); _d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice!, size, Format);
using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>(); using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
handleImport = new ImportMemoryWin32HandleInfoKHR handleImport = new ImportMemoryWin32HandleInfoKHR
@ -141,7 +141,7 @@ public unsafe class VulkanImage : IDisposable
MemorySize = memoryRequirements.Size; MemorySize = memoryRequirements.Size;
Api.BindImageMemory(_device, InternalHandle.Value, _imageMemory, 0).ThrowOnError(); Api.BindImageMemory(_device, InternalHandle, _imageMemory, 0).ThrowOnError();
var componentMapping = new ComponentMapping( var componentMapping = new ComponentMapping(
ComponentSwizzle.Identity, ComponentSwizzle.Identity,
ComponentSwizzle.Identity, ComponentSwizzle.Identity,
@ -155,7 +155,7 @@ public unsafe class VulkanImage : IDisposable
var imageViewCreateInfo = new ImageViewCreateInfo var imageViewCreateInfo = new ImageViewCreateInfo
{ {
SType = StructureType.ImageViewCreateInfo, SType = StructureType.ImageViewCreateInfo,
Image = InternalHandle.Value, Image = InternalHandle,
ViewType = ImageViewType.Type2D, ViewType = ImageViewType.Type2D,
Format = Format, Format = Format,
Components = componentMapping, Components = componentMapping,
@ -209,7 +209,7 @@ public unsafe class VulkanImage : IDisposable
ImageLayout fromLayout, AccessFlags fromAccessFlags, ImageLayout fromLayout, AccessFlags fromAccessFlags,
ImageLayout destinationLayout, AccessFlags destinationAccessFlags) ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
{ {
VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle.Value, VulkanMemoryHelper.TransitionLayout(Api, commandBuffer, InternalHandle,
fromLayout, fromLayout,
fromAccessFlags, fromAccessFlags,
destinationLayout, destinationAccessFlags, destinationLayout, destinationAccessFlags,
@ -241,8 +241,8 @@ public unsafe class VulkanImage : IDisposable
public unsafe void Dispose() public unsafe void Dispose()
{ {
Api.DestroyImageView(_device, _imageView.Value, null); Api.DestroyImageView(_device, _imageView, null);
Api.DestroyImage(_device, InternalHandle.Value, null); Api.DestroyImage(_device, InternalHandle, null);
Api.FreeMemory(_device, _imageMemory, null); Api.FreeMemory(_device, _imageMemory, null);
_imageView = default; _imageView = default;

2
samples/GpuInterop/VulkanDemo/VulkanSwapchain.cs

@ -146,6 +146,6 @@ class VulkanSwapchainImage : ISwapchainImage
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0); _lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
else else
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore, _availableSemaphore); _lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
} }
} }

37
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -42,23 +42,20 @@ namespace IntegrationTestApp
private void InitializeViewMenu() private void InitializeViewMenu()
{ {
var mainTabs = this.Get<TabControl>("MainTabs"); var mainTabs = this.Get<TabControl>("MainTabs");
var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1]; var viewMenu = (NativeMenuItem?)NativeMenu.GetMenu(this)?.Items[1];
if (mainTabs.Items is not null) foreach (var tabItem in mainTabs.Items.Cast<TabItem>())
{ {
foreach (TabItem tabItem in mainTabs.Items) var menuItem = new NativeMenuItem
{ {
var menuItem = new NativeMenuItem Header = (string?)tabItem.Header,
{ ToolTip = (string?)tabItem.Header,
Header = (string)tabItem.Header!, IsChecked = tabItem.IsSelected,
ToolTip = (string)tabItem.Header!, ToggleType = NativeMenuItemToggleType.Radio,
IsChecked = tabItem.IsSelected, };
ToggleType = NativeMenuItemToggleType.Radio,
}; menuItem.Click += (_, _) => tabItem.IsSelected = true;
viewMenu?.Menu?.Items.Add(menuItem);
menuItem.Click += (s, e) => tabItem.IsSelected = true;
viewMenu?.Menu?.Items.Add(menuItem);
}
} }
} }
@ -77,7 +74,7 @@ namespace IntegrationTestApp
var window = new ShowWindowTest var window = new ShowWindowTest
{ {
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex, WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
CanResize = canResizeCheckBox.IsChecked.Value, CanResize = canResizeCheckBox.IsChecked ?? false,
}; };
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
@ -227,9 +224,9 @@ namespace IntegrationTestApp
var gestureBorder2 = this.GetControl<Border>("GestureBorder2"); var gestureBorder2 = this.GetControl<Border>("GestureBorder2");
var lastGesture = this.GetControl<TextBlock>("LastGesture"); var lastGesture = this.GetControl<TextBlock>("LastGesture");
var resetGestures = this.GetControl<Button>("ResetGestures"); var resetGestures = this.GetControl<Button>("ResetGestures");
gestureBorder.Tapped += (s, e) => lastGesture.Text = "Tapped"; gestureBorder.Tapped += (_, _) => lastGesture.Text = "Tapped";
gestureBorder.DoubleTapped += (s, e) => gestureBorder.DoubleTapped += (_, _) =>
{ {
lastGesture.Text = "DoubleTapped"; lastGesture.Text = "DoubleTapped";
@ -238,14 +235,14 @@ namespace IntegrationTestApp
gestureBorder2.IsVisible = true; gestureBorder2.IsVisible = true;
}; };
gestureBorder2.DoubleTapped += (s, e) => gestureBorder2.DoubleTapped += (_, _) =>
{ {
lastGesture.Text = "DoubleTapped2"; lastGesture.Text = "DoubleTapped2";
}; };
Gestures.AddRightTappedHandler(gestureBorder, (s, e) => lastGesture.Text = "RightTapped"); Gestures.AddRightTappedHandler(gestureBorder, (_, _) => lastGesture.Text = "RightTapped");
resetGestures.Click += (s, e) => resetGestures.Click += (_, _) =>
{ {
lastGesture.Text = string.Empty; lastGesture.Text = string.Empty;
gestureBorder.IsVisible = true; gestureBorder.IsVisible = true;

12
src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs

@ -1,8 +1,4 @@
using System; #nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Android namespace Avalonia.Android
{ {
@ -11,9 +7,9 @@ namespace Avalonia.Android
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid(); protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
private static AppBuilder? s_appBuilder; private static AppBuilder? s_appBuilder;
internal static object ViewContent; internal static object? ViewContent;
public object Content public object? Content
{ {
get get
{ {
@ -51,7 +47,7 @@ namespace Avalonia.Android
View.Content = ViewContent; View.Content = ViewContent;
} }
if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime) if (Avalonia.Application.Current?.ApplicationLifetime is SingleViewLifetime lifetime)
{ {
lifetime.View = View; lifetime.View = View;
} }

15
src/Android/Avalonia.Android/AvaloniaMainActivity.cs

@ -1,9 +1,8 @@
using System; using System;
using System.Diagnostics; using System.Runtime.Versioning;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Content.Res;
using Android.OS; using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
@ -37,6 +36,7 @@ namespace Avalonia.Android
ActivityResult?.Invoke(requestCode, resultCode, data); ActivityResult?.Invoke(requestCode, resultCode, data);
} }
[SupportedOSPlatform("android23.0")]
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{ {
base.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
@ -47,7 +47,8 @@ namespace Avalonia.Android
public abstract partial class AvaloniaMainActivity<TApp> : AvaloniaMainActivity where TApp : Application, new() public abstract partial class AvaloniaMainActivity<TApp> : AvaloniaMainActivity where TApp : Application, new()
{ {
internal AvaloniaView View; internal AvaloniaView View { get; set; }
private GlobalLayoutListener _listener; private GlobalLayoutListener _listener;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
@ -68,9 +69,9 @@ namespace Avalonia.Android
base.OnResume(); base.OnResume();
// Android only respects LayoutInDisplayCutoutMode value if it has been set once before window becomes visible. // Android only respects LayoutInDisplayCutoutMode value if it has been set once before window becomes visible.
if (Build.VERSION.SdkInt >= BuildVersionCodes.P) if (OperatingSystem.IsAndroidVersionAtLeast(28) && Window is { Attributes: { } attributes })
{ {
Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges; attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
} }
} }
@ -83,9 +84,9 @@ namespace Avalonia.Android
base.OnDestroy(); base.OnDestroy();
} }
class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener private class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{ {
private AvaloniaView _view; private readonly AvaloniaView _view;
public GlobalLayoutListener(AvaloniaView view) public GlobalLayoutListener(AvaloniaView view)
{ {

2
src/Android/Avalonia.Android/AvaloniaView.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.Versioning;
using Android.Content; using Android.Content;
using Android.Content.Res; using Android.Content.Res;
using Android.Runtime; using Android.Runtime;
@ -46,6 +47,7 @@ namespace Avalonia.Android
return _view.View.DispatchKeyEvent(e); return _view.View.DispatchKeyEvent(e);
} }
[SupportedOSPlatform("android24.0")]
public override void OnVisibilityAggregated(bool isVisible) public override void OnVisibilityAggregated(bool isVisible)
{ {
base.OnVisibilityAggregated(isVisible); base.OnVisibilityAggregated(isVisible);

37
src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs

@ -30,17 +30,25 @@ namespace Avalonia.Android.Platform
{ {
_displayEdgeToEdge = value; _displayEdgeToEdge = value;
if(Build.VERSION.SdkInt >= BuildVersionCodes.P) var window = _activity.Window;
if (OperatingSystem.IsAndroidVersionAtLeast(28) && window?.Attributes is { } attributes)
{ {
_activity.Window.Attributes.LayoutInDisplayCutoutMode = value ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default; attributes.LayoutInDisplayCutoutMode = value ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default;
} }
WindowCompat.SetDecorFitsSystemWindows(_activity.Window, !value); if (window is not null)
{
WindowCompat.SetDecorFitsSystemWindows(_activity.Window, !value);
}
if(value) if(value)
{ {
_activity.Window.AddFlags(WindowManagerFlags.TranslucentStatus); if (window is not null)
_activity.Window.AddFlags(WindowManagerFlags.TranslucentNavigation); {
window.AddFlags(WindowManagerFlags.TranslucentStatus);
window.AddFlags(WindowManagerFlags.TranslucentNavigation);
}
} }
else else
{ {
@ -57,14 +65,17 @@ namespace Avalonia.Android.Platform
_callback.InsetsManager = this; _callback.InsetsManager = this;
ViewCompat.SetOnApplyWindowInsetsListener(_activity.Window.DecorView, this); if (_activity.Window is { } window)
{
ViewCompat.SetOnApplyWindowInsetsListener(window.DecorView, this);
ViewCompat.SetWindowInsetsAnimationCallback(_activity.Window.DecorView, _callback); ViewCompat.SetWindowInsetsAnimationCallback(window.DecorView, _callback);
}
if(Build.VERSION.SdkInt < BuildVersionCodes.R) if (Build.VERSION.SdkInt < BuildVersionCodes.R)
{ {
_usesLegacyLayouts = true; _usesLegacyLayouts = true;
_activity.Window.DecorView.ViewTreeObserver.AddOnGlobalLayoutListener(this); _activity.Window?.DecorView.ViewTreeObserver?.AddOnGlobalLayoutListener(this);
} }
DisplayEdgeToEdge = false; DisplayEdgeToEdge = false;
@ -74,7 +85,7 @@ namespace Avalonia.Android.Platform
{ {
get get
{ {
var insets = ViewCompat.GetRootWindowInsets(_activity.Window.DecorView); var insets = _activity.Window is { } window ? ViewCompat.GetRootWindowInsets(window.DecorView) : null;
if (insets != null) if (insets != null)
{ {
@ -127,7 +138,7 @@ namespace Avalonia.Android.Platform
return compat.AppearanceLightStatusBars ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark; return compat.AppearanceLightStatusBars ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark;
} }
catch (Exception _) catch (Exception)
{ {
return Controls.Platform.SystemBarTheme.Light; return Controls.Platform.SystemBarTheme.Light;
} }
@ -136,8 +147,6 @@ namespace Avalonia.Android.Platform
{ {
_statusBarTheme = value; _statusBarTheme = value;
var isDefault = _statusBarTheme == null;
if (!_topLevel.View.IsShown) if (!_topLevel.View.IsShown)
{ {
return; return;
@ -150,7 +159,7 @@ namespace Avalonia.Android.Platform
_isDefaultSystemBarLightTheme = compat.AppearanceLightStatusBars; _isDefaultSystemBarLightTheme = compat.AppearanceLightStatusBars;
} }
if (value == null && _isDefaultSystemBarLightTheme != null) if (value == null)
{ {
value = (bool)_isDefaultSystemBarLightTheme ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark; value = (bool)_isDefaultSystemBarLightTheme ? Controls.Platform.SystemBarTheme.Light : Controls.Platform.SystemBarTheme.Dark;
} }

8
src/Android/Avalonia.Android/Platform/AndroidSystemNavigationManager.cs

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Platform; using Avalonia.Platform;
@ -6,7 +8,7 @@ namespace Avalonia.Android.Platform
{ {
internal class AndroidSystemNavigationManagerImpl : ISystemNavigationManagerImpl internal class AndroidSystemNavigationManagerImpl : ISystemNavigationManagerImpl
{ {
public event EventHandler<RoutedEventArgs> BackRequested; public event EventHandler<RoutedEventArgs>? BackRequested;
public AndroidSystemNavigationManagerImpl(IActivityNavigationService? navigationService) public AndroidSystemNavigationManagerImpl(IActivityNavigationService? navigationService)
{ {
@ -16,7 +18,7 @@ namespace Avalonia.Android.Platform
} }
} }
private void OnBackRequested(object sender, AndroidBackRequestedEventArgs e) private void OnBackRequested(object? sender, AndroidBackRequestedEventArgs e)
{ {
var routedEventArgs = new RoutedEventArgs(); var routedEventArgs = new RoutedEventArgs();

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

@ -1,8 +1,8 @@
#nullable enable
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.Content; using Android.Content;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
@ -10,34 +10,34 @@ namespace Avalonia.Android.Platform
{ {
internal class ClipboardImpl : IClipboard internal class ClipboardImpl : IClipboard
{ {
private ClipboardManager? _clipboardManager; private readonly ClipboardManager? _clipboardManager;
internal ClipboardImpl(ClipboardManager? value) internal ClipboardImpl(ClipboardManager? value)
{ {
_clipboardManager = value; _clipboardManager = value;
} }
public Task<string> GetTextAsync() public Task<string?> GetTextAsync()
{ {
if (_clipboardManager?.HasPrimaryClip == true) if (_clipboardManager?.HasPrimaryClip == true)
{ {
return Task.FromResult<string>(_clipboardManager.PrimaryClip.GetItemAt(0).Text); return Task.FromResult(_clipboardManager.PrimaryClip?.GetItemAt(0)?.Text);
} }
return Task.FromResult<string>(null); return Task.FromResult<string?>(null);
} }
public Task SetTextAsync(string text) public Task SetTextAsync(string? text)
{ {
if(_clipboardManager == null) if(_clipboardManager == null)
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
ClipData clip = ClipData.NewPlainText("text", text); var clip = ClipData.NewPlainText("text", text);
_clipboardManager.PrimaryClip = clip; _clipboardManager.PrimaryClip = clip;
return Task.FromResult<object>(null); return Task.CompletedTask;
} }
public Task ClearAsync() public Task ClearAsync()
@ -49,13 +49,13 @@ namespace Avalonia.Android.Platform
_clipboardManager.PrimaryClip = null; _clipboardManager.PrimaryClip = null;
return Task.FromResult<object>(null); return Task.CompletedTask;
} }
public Task SetDataObjectAsync(IDataObject data) => throw new PlatformNotSupportedException(); public Task SetDataObjectAsync(IDataObject data) => throw new PlatformNotSupportedException();
public Task<string[]> GetFormatsAsync() => throw new PlatformNotSupportedException(); public Task<string[]> GetFormatsAsync() => throw new PlatformNotSupportedException();
public Task<object> GetDataAsync(string format) => throw new PlatformNotSupportedException(); public Task<object?> GetDataAsync(string format) => throw new PlatformNotSupportedException();
} }
} }

4
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -347,7 +347,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
activity.SetTranslucent(true); activity.SetTranslucent(true);
SetBlurBehind(activity, 0); SetBlurBehind(activity, 0);
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent)); activity.Window?.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
} }
} }
else if (level == WindowTransparencyLevel.Blur) else if (level == WindowTransparencyLevel.Blur)
@ -448,8 +448,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
private readonly AvaloniaInputConnection _inputConnection; private readonly AvaloniaInputConnection _inputConnection;
public event EventHandler<int> SelectionChanged;
public EditableWrapper(AvaloniaInputConnection inputConnection) public EditableWrapper(AvaloniaInputConnection inputConnection)
{ {
_inputConnection = inputConnection; _inputConnection = inputConnection;

8
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -55,7 +55,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey); var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey);
var rawKeyEvent = new RawKeyEventArgs( var rawKeyEvent = new RawKeyEventArgs(
AndroidKeyboardDevice.Instance, AndroidKeyboardDevice.Instance!,
Convert.ToUInt64(e.EventTime), Convert.ToUInt64(e.EventTime),
_view.InputRoot, _view.InputRoot,
e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
@ -64,18 +64,18 @@ namespace Avalonia.Android.Platform.Specific.Helpers
physicalKey, physicalKey,
keySymbol); keySymbol);
_view.Input(rawKeyEvent); _view.Input?.Invoke(rawKeyEvent);
if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32) if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
|| unicodeTextInput != null) || unicodeTextInput != null)
{ {
var rawTextEvent = new RawTextInputEventArgs( var rawTextEvent = new RawTextInputEventArgs(
AndroidKeyboardDevice.Instance, AndroidKeyboardDevice.Instance!,
Convert.ToUInt64(e.EventTime), Convert.ToUInt64(e.EventTime),
_view.InputRoot, _view.InputRoot,
unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString() unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
); );
_view.Input(rawTextEvent); _view.Input?.Invoke(rawTextEvent);
} }
if (e.Action == KeyEventActions.Up) if (e.Action == KeyEventActions.Up)

45
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -172,29 +172,29 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
{ {
} }
public async Task<IStorageFile?> CreateFileAsync(string name) public Task<IStorageFile?> CreateFileAsync(string name)
{ {
var mimeType = MimeTypeMap.Singleton?.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(name)) ?? "application/octet-stream"; var mimeType = MimeTypeMap.Singleton?.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(name)) ?? "application/octet-stream";
var newFile = Document.CreateFile(mimeType, name); var newFile = Document?.CreateFile(mimeType, name);
if(newFile == null) if(newFile == null)
{ {
return null; return Task.FromResult<IStorageFile?>(null);
} }
return new AndroidStorageFile(Activity, newFile.Uri, this); return Task.FromResult<IStorageFile?>(new AndroidStorageFile(Activity, newFile.Uri, this));
} }
public async Task<IStorageFolder?> CreateFolderAsync(string name) public Task<IStorageFolder?> CreateFolderAsync(string name)
{ {
var newFolder = Document?.CreateDirectory(name); var newFolder = Document?.CreateDirectory(name);
if (newFolder == null) if (newFolder == null)
{ {
return null; return Task.FromResult<IStorageFolder?>(null);
} }
return new AndroidStorageFolder(Activity, newFolder.Uri, false, this, PermissionRoot); return Task.FromResult<IStorageFolder?>(new AndroidStorageFolder(Activity, newFolder.Uri, false, this, PermissionRoot));
} }
public override async Task DeleteAsync() public override async Task DeleteAsync()
@ -286,15 +286,15 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
return null; return null;
async Task<AndroidStorageFolder?> MoveRecursively(AndroidStorageFolder storageFolder, AndroidStorageFolder destination) static async Task<AndroidStorageFolder?> MoveRecursively(AndroidStorageFolder storageFolder, AndroidStorageFolder destination)
{ {
destination = await destination.CreateFolderAsync(storageFolder.Name) as AndroidStorageFolder; if (await destination.CreateFolderAsync(storageFolder.Name) is not AndroidStorageFolder newDestination)
if (destination == null)
{ {
return null; return null;
} }
destination = newDestination;
await foreach (var file in storageFolder.GetItemsAsync()) await foreach (var file in storageFolder.GetItemsAsync())
{ {
if (file is AndroidStorageFolder folder) if (file is AndroidStorageFolder folder)
@ -477,25 +477,32 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
if (Activity != null && destination is AndroidStorageFolder storageFolder) if (Activity != null && destination is AndroidStorageFolder storageFolder)
{ {
if (Build.VERSION.SdkInt >= BuildVersionCodes.N) AndroidUri? movedUri = null;
if (OperatingSystem.IsAndroidVersionAtLeast(24))
{ {
try try
{ {
var uri = DocumentsContract.MoveDocument(Activity.ContentResolver!, Uri, ((await GetParentAsync()) as AndroidStorageFolder)!.Uri, storageFolder.Document!.Uri); if (Activity.ContentResolver is { } contentResolver &&
storageFolder.Document?.Uri is { } targetParentUri &&
return new AndroidStorageFile(Activity, uri, storageFolder); await GetParentAsync() is AndroidStorageFolder parentFolder)
{
movedUri = DocumentsContract.MoveDocument(contentResolver, Uri, parentFolder.Uri, targetParentUri);
}
} }
catch (Exception ex) catch (Exception)
{ {
// There are many reason why DocumentContract will fail to move a file. We fallback to copying. // There are many reason why DocumentContract will fail to move a file. We fallback to copying.
return await MoveFileByCopy(); return await MoveFileByCopy();
} }
} }
else
if (movedUri is not null)
{ {
return await MoveFileByCopy(); return new AndroidStorageFile(Activity, movedUri, storageFolder);
} }
return await MoveFileByCopy();
} }
async Task<AndroidStorageFile?> MoveFileByCopy() async Task<AndroidStorageFile?> MoveFileByCopy()

4
src/Avalonia.Base/Platform/Interop/Utf8Buffer.cs

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using System.Buffers; using System.Buffers;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;

2
src/Tizen/Avalonia.Tizen/Avalonia.Tizen.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0-tizen</TargetFrameworks> <TargetFramework>net6.0-tizen</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>

43
src/Tizen/Avalonia.Tizen/NuiAvaloniaView.cs

@ -3,11 +3,9 @@ using Avalonia.Controls.Embedding;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Server; using Avalonia.Rendering.Composition.Server;
using Avalonia.Threading;
using Tizen.NUI; using Tizen.NUI;
using Tizen.NUI.BaseComponents; using Tizen.NUI.BaseComponents;
@ -22,21 +20,32 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
private readonly NuiTouchHandler _touchHandler; private readonly NuiTouchHandler _touchHandler;
private readonly NuiAvaloniaViewTextEditable _textEditor; private readonly NuiAvaloniaViewTextEditable _textEditor;
private TizenRenderTimer? _renderTimer; private TizenRenderTimer? _renderTimer;
private TopLevelImpl _topLevelImpl; private TopLevelImpl? _topLevelImpl;
private EmbeddableControlRoot _topLevel; private EmbeddableControlRoot? _topLevel;
private TouchDevice _device = new(); private readonly TouchDevice _device = new();
private ServerCompositionTarget _compositionTargetServer; private ServerCompositionTarget? _compositionTargetServer;
private IInputRoot? _inputRoot;
public IInputRoot InputRoot { get; set; }
public INativeControlHostImpl NativeControlHost { get; } public INativeControlHostImpl NativeControlHost { get; }
internal TopLevelImpl TopLevelImpl => _topLevelImpl;
public double Scaling => 1; public double Scaling => 1;
public Size ClientSize => new(Size.Width, Size.Height); public Size ClientSize => new(Size.Width, Size.Height);
public IInputRoot InputRoot
{
get => _inputRoot ?? throw new InvalidOperationException($"{nameof(InputRoot)} hasn't been set");
set => _inputRoot = value;
}
private TopLevel TopLevel
=> _topLevel ?? throw new InvalidOperationException($"{nameof(NuiAvaloniaView)} hasn't been initialized");
internal TopLevelImpl TopLevelImpl
=> _topLevelImpl ?? throw new InvalidOperationException($"{nameof(NuiAvaloniaView)} hasn't been initialized");
public Control? Content public Control? Content
{ {
get => _topLevel.Content as Control; get => TopLevel.Content as Control;
set => _topLevel.Content = value; set => TopLevel.Content = value;
} }
internal NuiAvaloniaViewTextEditable TextEditor => _textEditor; internal NuiAvaloniaViewTextEditable TextEditor => _textEditor;
@ -44,7 +53,7 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
#region Setup #region Setup
public event Action OnSurfaceInit; public event Action? OnSurfaceInit;
public NuiAvaloniaView() : base(ColorFormat.RGBA8888) public NuiAvaloniaView() : base(ColorFormat.RGBA8888)
{ {
@ -73,7 +82,7 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
private int GlRenderFrame() private int GlRenderFrame()
{ {
if (_renderTimer == null || _topLevel == null) if (_renderTimer == null || _compositionTargetServer == null)
return 0; return 0;
var rev = _compositionTargetServer.Revision; var rev = _compositionTargetServer.Revision;
@ -141,13 +150,13 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
private bool OnTouchEvent(object source, TouchEventArgs e) private bool OnTouchEvent(object source, TouchEventArgs e)
{ {
_touchHandler?.Handle(e); _touchHandler.Handle(e);
return true; return true;
} }
private bool OnWheelEvent(object source, WheelEventArgs e) private bool OnWheelEvent(object source, WheelEventArgs e)
{ {
_touchHandler?.Handle(e); _touchHandler.Handle(e);
return true; return true;
} }
@ -169,9 +178,9 @@ public class NuiAvaloniaView : GLView, ITizenView, ITextInputMethodImpl
{ {
if (disposing) if (disposing)
{ {
_topLevel.StopRendering(); _topLevel?.StopRendering();
_topLevel.Dispose(); _topLevel?.Dispose();
_topLevelImpl.Dispose(); _topLevelImpl?.Dispose();
_device.Dispose(); _device.Dispose();
} }
base.Dispose(disposing); base.Dispose(disposing);

2
src/Tizen/Avalonia.Tizen/NuiAvaloniaViewTextEditable.cs

@ -20,9 +20,7 @@ internal class NuiAvaloniaViewTextEditable
public bool IsActive => _client != null && _keyboardPresented; public bool IsActive => _client != null && _keyboardPresented;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public NuiAvaloniaViewTextEditable(NuiAvaloniaView avaloniaView) public NuiAvaloniaViewTextEditable(NuiAvaloniaView avaloniaView)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{ {
_avaloniaView = avaloniaView; _avaloniaView = avaloniaView;
_singleLineTextInput = new NuiSingleLineTextInput _singleLineTextInput = new NuiSingleLineTextInput

4
src/Tizen/Avalonia.Tizen/NuiGlPlatform.cs

@ -54,7 +54,7 @@ class GlContext : IGlContext
public bool IsSharedWith(IGlContext context) => true; public bool IsSharedWith(IGlContext context) => true;
public bool CanCreateSharedContext => true; public bool CanCreateSharedContext => true;
public IGlContext CreateSharedContext(IEnumerable<GlVersion> preferredVersions = null) public IGlContext CreateSharedContext(IEnumerable<GlVersion>? preferredVersions = null)
{ {
return this; return this;
} }
@ -78,7 +78,7 @@ class GlContext : IGlContext
} }
} }
public object TryGetFeature(Type featureType) => null; public object? TryGetFeature(Type featureType) => null;
public IntPtr GetProcAddress(string procName) public IntPtr GetProcAddress(string procName)
{ {

13
src/Tizen/Avalonia.Tizen/NuiTizenApplication.cs

@ -3,7 +3,6 @@ using Avalonia.Controls.ApplicationLifetimes;
using Tizen.NUI; using Tizen.NUI;
using Window = Tizen.NUI.Window; using Window = Tizen.NUI.Window;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Threading;
namespace Avalonia.Tizen; namespace Avalonia.Tizen;
@ -23,7 +22,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
View = view; View = view;
} }
public Control MainView public Control? MainView
{ {
get => View.Content; get => View.Content;
set => View.Content = value; set => View.Content = value;
@ -37,9 +36,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Creating application"); Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Creating application");
base.OnCreate(); base.OnCreate();
#pragma warning disable CS8601 // Possible null reference assignment. TizenThreadingInterface.MainloopContext = SynchronizationContext.Current!;
TizenThreadingInterface.MainloopContext = SynchronizationContext.Current;
#pragma warning restore CS8601 // Possible null reference assignment.
Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup view"); Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup view");
_lifetime = new SingleViewLifetime(new NuiAvaloniaView()); _lifetime = new SingleViewLifetime(new NuiAvaloniaView());
@ -50,7 +47,7 @@ public class NuiTizenApplication<TApp> : NUIApplication
Window.Instance.RenderingBehavior = RenderingBehaviorType.Continuously; Window.Instance.RenderingBehavior = RenderingBehaviorType.Continuously;
Window.Instance.GetDefaultLayer().Add(_lifetime.View); Window.Instance.GetDefaultLayer().Add(_lifetime.View);
Window.Instance.KeyEvent += (s, e) => _lifetime?.View?.KeyboardHandler.Handle(e); Window.Instance.KeyEvent += (_, e) => _lifetime?.View.KeyboardHandler.Handle(e);
} }
private void ContinueSetupApplication() private void ContinueSetupApplication()
@ -64,10 +61,10 @@ public class NuiTizenApplication<TApp> : NUIApplication
{ {
CustomizeAppBuilder(builder); CustomizeAppBuilder(builder);
builder.AfterSetup(_ => _lifetime.View.Initialise()); builder.AfterSetup(_ => _lifetime!.View.Initialise());
Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup lifetime"); Logger.TryGet(LogEventLevel.Debug, LogKey)?.Log(null, "Setup lifetime");
builder.SetupWithLifetime(_lifetime); builder.SetupWithLifetime(_lifetime!);
}, null); }, null);
} }
} }

34
src/Tizen/Avalonia.Tizen/Platform/Permissions.cs

@ -1,5 +1,4 @@
using System.Security; using System.Security;
using System.Security.Permissions;
using Tizen.Applications; using Tizen.Applications;
using Tizen.Security; using Tizen.Security;
@ -33,7 +32,7 @@ internal class Permissions
} }
} }
public static bool IsPrivilegeDeclared(string tizenPrivilege) public static bool IsPrivilegeDeclared(string? tizenPrivilege)
{ {
var tizenPrivileges = tizenPrivilege; var tizenPrivileges = tizenPrivilege;
@ -48,21 +47,21 @@ internal class Permissions
return true; return true;
} }
public static void EnsureDeclared(params Privilege[] requiredPrivileges) public static void EnsureDeclared(params Privilege[]? requiredPrivileges)
{ {
if (requiredPrivileges?.Any() != true) if (requiredPrivileges?.Any() != true)
return; return;
foreach (var (tizenPrivilege, isRuntime) in requiredPrivileges) foreach (var (tizenPrivilege, _) in requiredPrivileges)
{ {
if (!IsPrivilegeDeclared(tizenPrivilege)) if (!IsPrivilegeDeclared(tizenPrivilege))
throw new SecurityException($"You need to declare the privilege: `{tizenPrivilege}` in your tizen-manifest.xml"); throw new SecurityException($"You need to declare the privilege: `{tizenPrivilege}` in your tizen-manifest.xml");
} }
} }
public static Task<bool> CheckPrivilegeAsync(params Privilege[] requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, false); public static Task<bool> CheckPrivilegeAsync(params Privilege[]? requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, false);
public static Task<bool> RequestPrivilegeAsync(params Privilege[] requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, true); public static Task<bool> RequestPrivilegeAsync(params Privilege[]? requiredPrivileges) => CheckPrivilegeAsync(requiredPrivileges, true);
private static async Task<bool> CheckPrivilegeAsync(Privilege[] requiredPrivileges, bool ask) private static async Task<bool> CheckPrivilegeAsync(Privilege[]? requiredPrivileges, bool ask)
{ {
var ret = global::Tizen.System.Information.TryGetValue("http://tizen.org/feature/profile", out string profile); var ret = global::Tizen.System.Information.TryGetValue("http://tizen.org/feature/profile", out string profile);
if (!ret || (ret && (!profile.Equals("mobile") || !profile.Equals("wearable")))) if (!ret || (ret && (!profile.Equals("mobile") || !profile.Equals("wearable"))))
@ -77,7 +76,7 @@ internal class Permissions
var tizenPrivileges = requiredPrivileges.Where(p => p.IsRuntime); var tizenPrivileges = requiredPrivileges.Where(p => p.IsRuntime);
foreach (var (tizenPrivilege, isRuntime) in tizenPrivileges) foreach (var (tizenPrivilege, _) in tizenPrivileges)
{ {
var checkResult = PrivacyPrivilegeManager.CheckPermission(tizenPrivilege); var checkResult = PrivacyPrivilegeManager.CheckPermission(tizenPrivilege);
if (checkResult == CheckResult.Ask) if (checkResult == CheckResult.Ask)
@ -87,16 +86,21 @@ internal class Permissions
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
PrivacyPrivilegeManager.GetResponseContext(tizenPrivilege) PrivacyPrivilegeManager.GetResponseContext(tizenPrivilege)
.TryGetTarget(out var context); .TryGetTarget(out var context);
void OnResponseFetched(object sender, RequestResponseEventArgs e)
void OnResponseFetched(object? sender, RequestResponseEventArgs e)
{ {
tcs.TrySetResult(e.result == RequestResult.AllowForever); tcs.TrySetResult(e.result == RequestResult.AllowForever);
} }
context.ResponseFetched += OnResponseFetched;
PrivacyPrivilegeManager.RequestPermission(tizenPrivilege); if (context != null)
var result = await tcs.Task; {
context.ResponseFetched -= OnResponseFetched; context.ResponseFetched += OnResponseFetched;
if (result) PrivacyPrivilegeManager.RequestPermission(tizenPrivilege);
continue; var result = await tcs.Task;
context.ResponseFetched -= OnResponseFetched;
if (result)
continue;
}
} }
return false; return false;
} }

2
src/Tizen/Avalonia.Tizen/Stubs.cs

@ -9,7 +9,7 @@ internal class WindowingPlatformStub : IWindowingPlatform
public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
public ITrayIconImpl CreateTrayIcon() => null; public ITrayIconImpl? CreateTrayIcon() => null;
} }
internal class PlatformIconLoaderStub : IPlatformIconLoader internal class PlatformIconLoaderStub : IPlatformIconLoader

22
src/Tizen/Avalonia.Tizen/TizenPlatform.cs

@ -1,21 +1,33 @@
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Tizen.Platform.Input; using Avalonia.Tizen.Platform.Input;
using Avalonia.Tizen.Platform; using Avalonia.Tizen.Platform;
using Avalonia.Threading;
namespace Avalonia.Tizen; namespace Avalonia.Tizen;
internal class TizenPlatform internal class TizenPlatform
{ {
public static readonly TizenPlatform Instance = new(); public static readonly TizenPlatform Instance = new();
internal static NuiGlPlatform GlPlatform { get; set; }
internal static Compositor Compositor { get; private set; } private static NuiGlPlatform? s_glPlatform;
internal static TizenThreadingInterface ThreadingInterface { get; private set; } = new(); private static Compositor? s_compositor;
internal static NuiGlPlatform GlPlatform
{
get => s_glPlatform ?? throw new InvalidOperationException($"{nameof(TizenPlatform)} hasn't been initialized");
private set => s_glPlatform = value;
}
internal static Compositor Compositor
{
get => s_compositor ?? throw new InvalidOperationException($"{nameof(TizenPlatform)} hasn't been initialized");
private set => s_compositor = value;
}
internal static TizenThreadingInterface ThreadingInterface { get; } = new();
public static void Initialize() public static void Initialize()
{ {

10
src/Tizen/Avalonia.Tizen/TizenThreadingInterface.cs

@ -7,9 +7,13 @@ internal class TizenThreadingInterface : IPlatformThreadingInterface
internal event Action? TickExecuted; internal event Action? TickExecuted;
private bool _signaled; private bool _signaled;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private static SynchronizationContext? s_mainloopContext;
internal static SynchronizationContext MainloopContext { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. internal static SynchronizationContext MainloopContext
{
get => s_mainloopContext ?? throw new InvalidOperationException($"{nameof(MainloopContext)} hasn't been set");
set => s_mainloopContext = value;
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{ {

2
src/Tizen/Avalonia.Tizen/TopLevelImpl.cs

@ -110,7 +110,7 @@ internal class TopLevelImpl : ITopLevelImpl
internal void TextInput(string text) internal void TextInput(string text)
{ {
if (Input == null) return; if (Input == null) return;
var args = new RawTextInputEventArgs(TizenKeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, _view.InputRoot, text); var args = new RawTextInputEventArgs(TizenKeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _view.InputRoot, text);
Input(args); Input(args);
} }

70
src/iOS/Avalonia.iOS/AvaloniaView.cs

@ -1,3 +1,5 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Controls; using Avalonia.Controls;
@ -12,7 +14,6 @@ using Avalonia.Input.TextInput;
using Avalonia.iOS.Storage; using Avalonia.iOS.Storage;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using CoreAnimation; using CoreAnimation;
using Foundation; using Foundation;
@ -25,12 +26,15 @@ namespace Avalonia.iOS
{ {
public partial class AvaloniaView : UIView, ITextInputMethodImpl public partial class AvaloniaView : UIView, ITextInputMethodImpl
{ {
internal IInputRoot InputRoot { get; private set; } internal IInputRoot InputRoot
private TopLevelImpl _topLevelImpl; => _inputRoot ?? throw new InvalidOperationException($"{nameof(IWindowImpl.SetInputRoot)} must have been called");
private EmbeddableControlRoot _topLevel;
private TouchHandler _touches; private readonly TopLevelImpl _topLevelImpl;
private TextInputMethodClient _client; private readonly EmbeddableControlRoot _topLevel;
private IAvaloniaViewController _controller; private readonly TouchHandler _touches;
private TextInputMethodClient? _client;
private IAvaloniaViewController? _controller;
private IInputRoot? _inputRoot;
public AvaloniaView() public AvaloniaView()
{ {
@ -60,7 +64,7 @@ namespace Avalonia.iOS
public override bool CanResignFirstResponder => true; public override bool CanResignFirstResponder => true;
/// <inheritdoc /> /// <inheritdoc />
public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection) public override void TraitCollectionDidChange(UITraitCollection? previousTraitCollection)
{ {
base.TraitCollectionDidChange(previousTraitCollection); base.TraitCollectionDidChange(previousTraitCollection);
@ -91,7 +95,7 @@ namespace Avalonia.iOS
private readonly IStorageProvider _storageProvider; private readonly IStorageProvider _storageProvider;
internal readonly InsetsManager _insetsManager; internal readonly InsetsManager _insetsManager;
private readonly ClipboardImpl _clipboard; private readonly ClipboardImpl _clipboard;
private IDisposable _paddingInsets; private IDisposable? _paddingInsets;
public AvaloniaView View => _view; public AvaloniaView View => _view;
@ -101,17 +105,17 @@ namespace Avalonia.iOS
_nativeControlHost = new NativeControlHostImpl(view); _nativeControlHost = new NativeControlHostImpl(view);
_storageProvider = new IOSStorageProvider(view); _storageProvider = new IOSStorageProvider(view);
_insetsManager = new InsetsManager(view); _insetsManager = new InsetsManager(view);
_insetsManager.DisplayEdgeToEdgeChanged += (sender, edgeToEdge) => _insetsManager.DisplayEdgeToEdgeChanged += (_, edgeToEdge) =>
{ {
// iOS doesn't add any paddings/margins to the application by itself. // iOS doesn't add any paddings/margins to the application by itself.
// Application is fully responsible for safe area paddings. // Application is fully responsible for safe area paddings.
// So, unlikely to android, we need to "fake" safe area insets when edge to edge is disabled. // So, unlikely to android, we need to "fake" safe area insets when edge to edge is disabled.
_paddingInsets?.Dispose(); _paddingInsets?.Dispose();
if (!edgeToEdge) if (!edgeToEdge && view._controller is { } controller)
{ {
_paddingInsets = view._topLevel.SetValue( _paddingInsets = view._topLevel.SetValue(
TemplatedControl.PaddingProperty, TemplatedControl.PaddingProperty,
view._controller.SafeAreaPadding, controller.SafeAreaPadding,
BindingPriority.Style); // lower priority, so it can be redefined by user BindingPriority.Style); // lower priority, so it can be redefined by user
} }
}; };
@ -132,19 +136,19 @@ namespace Avalonia.iOS
public void SetInputRoot(IInputRoot inputRoot) public void SetInputRoot(IInputRoot inputRoot)
{ {
_view.InputRoot = inputRoot; _view._inputRoot = inputRoot;
} }
public Point PointToClient(PixelPoint point) => new Point(point.X, point.Y); public Point PointToClient(PixelPoint point) => new Point(point.X, point.Y);
public PixelPoint PointToScreen(Point point) => new PixelPoint((int)point.X, (int)point.Y); public PixelPoint PointToScreen(Point point) => new PixelPoint((int)point.X, (int)point.Y);
public void SetCursor(ICursorImpl _) public void SetCursor(ICursorImpl? cursor)
{ {
// no-op // no-op
} }
public IPopupImpl CreatePopup() public IPopupImpl? CreatePopup()
{ {
// In-window popups // In-window popups
return null; return null;
@ -158,18 +162,16 @@ namespace Avalonia.iOS
public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height); public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height);
public Size? FrameSize => null; public Size? FrameSize => null;
public double RenderScaling => _view.ContentScaleFactor; public double RenderScaling => _view.ContentScaleFactor;
public IEnumerable<object> Surfaces { get; set; } public IEnumerable<object> Surfaces { get; set; } = Array.Empty<object>();
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, WindowResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public Action Closed { get; set; } public Action? Closed { get; set; }
public Action LostFocus { get; set; } public Action? LostFocus { get; set; }
// legacy no-op
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None; public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
@ -226,13 +228,13 @@ namespace Avalonia.iOS
return new Class(typeof(CAEAGLLayer)); return new Class(typeof(CAEAGLLayer));
} }
public override void TouchesBegan(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt); public override void TouchesBegan(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
public override void TouchesMoved(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt); public override void TouchesMoved(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
public override void TouchesEnded(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt); public override void TouchesEnded(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
public override void TouchesCancelled(NSSet touches, UIEvent evt) => _touches.Handle(touches, evt); public override void TouchesCancelled(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
public override void LayoutSubviews() public override void LayoutSubviews()
{ {
@ -240,9 +242,9 @@ namespace Avalonia.iOS
base.LayoutSubviews(); base.LayoutSubviews();
} }
public Control Content public Control? Content
{ {
get => (Control)_topLevel.Content; get => (Control?)_topLevel.Content;
set => _topLevel.Content = value; set => _topLevel.Content = value;
} }
} }

2
src/iOS/Avalonia.iOS/Storage/IOSSecurityScopedStream.cs

@ -17,7 +17,7 @@ internal sealed class IOSSecurityScopedStream : Stream
internal IOSSecurityScopedStream(NSUrl url, FileAccess access) internal IOSSecurityScopedStream(NSUrl url, FileAccess access)
{ {
_document = new UIDocument(url); _document = new UIDocument(url);
var path = _document.FileUrl.Path; var path = _document.FileUrl.Path!;
_url = url; _url = url;
_url.StartAccessingSecurityScopedResource(); _url.StartAccessingSecurityScopedResource();
_stream = File.Open(path, FileMode.Open, access); _stream = File.Open(path, FileMode.Open, access);

15
src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs

@ -47,7 +47,12 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
Logger.TryGet(LogEventLevel.Error, LogArea.IOSPlatform)?. Logger.TryGet(LogEventLevel.Error, LogArea.IOSPlatform)?.
Log(this, "GetBasicPropertiesAsync returned an error: {ErrorCode} {ErrorMessage}", error.Code, error.LocalizedFailureReason); Log(this, "GetBasicPropertiesAsync returned an error: {ErrorCode} {ErrorMessage}", error.Code, error.LocalizedFailureReason);
} }
return Task.FromResult(new StorageItemProperties(attributes?.Size, (DateTime)attributes?.CreationDate, (DateTime)attributes?.ModificationDate));
var properties = attributes is null ?
new StorageItemProperties() :
new StorageItemProperties(attributes.Size, (DateTime)attributes.CreationDate, (DateTime)attributes.ModificationDate);
return Task.FromResult(properties);
} }
public Task<IStorageFolder?> GetParentAsync() public Task<IStorageFolder?> GetParentAsync()
@ -62,7 +67,7 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
: Task.FromException(new NSErrorException(error)); : Task.FromException(new NSErrorException(error));
} }
public async Task<IStorageItem?> MoveAsync(IStorageFolder destination) public Task<IStorageItem?> MoveAsync(IStorageFolder destination)
{ {
if (destination is not IOSStorageFolder folder) if (destination is not IOSStorageFolder folder)
{ {
@ -75,8 +80,8 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
if (NSFileManager.DefaultManager.Move(folder.Url, newPath, out var error)) if (NSFileManager.DefaultManager.Move(folder.Url, newPath, out var error))
{ {
return isDir return isDir
? new IOSStorageFolder(newPath) ? Task.FromResult<IStorageItem?>(new IOSStorageFolder(newPath))
: new IOSStorageFile(newPath); : Task.FromResult<IStorageItem?>(new IOSStorageFile(newPath));
} }
if (error is not null) if (error is not null)
@ -84,7 +89,7 @@ internal abstract class IOSStorageItem : IStorageBookmarkItem
throw new NSErrorException(error); throw new NSErrorException(error);
} }
return null; return Task.FromResult<IStorageItem?>(null);
} }
public Task ReleaseBookmarkAsync() public Task ReleaseBookmarkAsync()

13
src/iOS/Avalonia.iOS/TextInputResponder.cs

@ -159,17 +159,20 @@ partial class AvaloniaView
{ {
Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering key press {key}", key); Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering key press {key}", key);
_view._topLevelImpl.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, if (_view._topLevelImpl.Input is { } input)
RawKeyEventType.KeyDown, key, RawInputModifiers.None, physicalKey, keySymbol)); {
input.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
RawKeyEventType.KeyDown, key, RawInputModifiers.None, physicalKey, keySymbol));
_view._topLevelImpl.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, input.Invoke(new RawKeyEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot,
RawKeyEventType.KeyUp, key, RawInputModifiers.None, physicalKey, keySymbol)); RawKeyEventType.KeyUp, key, RawInputModifiers.None, physicalKey, keySymbol));
}
} }
private void TextInput(string text) private void TextInput(string text)
{ {
Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering text input {text}", text); Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "Triggering text input {text}", text);
_view._topLevelImpl.Input(new RawTextInputEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, text)); _view._topLevelImpl.Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice.Instance!, 0, _view.InputRoot, text));
} }
void IUIKeyInput.InsertText(string text) void IUIKeyInput.InsertText(string text)

11
tests/Avalonia.Base.UnitTests/DispatcherTests.cs

@ -14,8 +14,8 @@ public class DispatcherTests
{ {
class SimpleDispatcherImpl : IDispatcherImpl, IDispatcherImplWithPendingInput class SimpleDispatcherImpl : IDispatcherImpl, IDispatcherImplWithPendingInput
{ {
private Thread _loopThread = Thread.CurrentThread; private readonly Thread _loopThread = Thread.CurrentThread;
private object _lock = new(); private readonly object _lock = new();
public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _loopThread; public bool CurrentThreadIsLoopThread => Thread.CurrentThread == _loopThread;
public void Signal() public void Signal()
{ {
@ -221,7 +221,8 @@ public class DispatcherTests
0 => 1, 0 => 1,
1 => 4, 1 => 4,
2 => 8, 2 => 8,
3 => 10 3 => 10,
_ => throw new InvalidOperationException($"Unexpected value {c}")
}; };
Assert.Equal(Enumerable.Range(0, expectedCount), actions); Assert.Equal(Enumerable.Range(0, expectedCount), actions);
@ -380,7 +381,7 @@ public class DispatcherTests
class WaitHelper : SynchronizationContext, NonPumpingLockHelper.IHelperImpl class WaitHelper : SynchronizationContext, NonPumpingLockHelper.IHelperImpl
{ {
public int WaitCount; public int WaitCount;
public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{ {
WaitCount++; WaitCount++;
return base.Wait(waitHandles, waitAll, millisecondsTimeout); return base.Wait(waitHandles, waitAll, millisecondsTimeout);
@ -502,4 +503,4 @@ public class DispatcherTests
t.GetAwaiter().GetResult(); t.GetAwaiter().GetResult();
} }
} }
} }

13
tests/Avalonia.Base.UnitTests/Media/EffectTests.cs

@ -25,9 +25,8 @@ public class EffectTests
InlineData("drop-shadow ( 10 20 30 #ffff00ff ) ", 10, 20, 30, 0xffff00ff), InlineData("drop-shadow ( 10 20 30 #ffff00ff ) ", 10, 20, 30, 0xffff00ff),
InlineData("drop-shadow(10 20 30 red)", 10, 20, 30, 0xffff0000), InlineData("drop-shadow(10 20 30 red)", 10, 20, 30, 0xffff0000),
InlineData("drop-shadow ( 10 20 30 red ) ", 10, 20, 30, 0xffff0000), InlineData("drop-shadow ( 10 20 30 red ) ", 10, 20, 30, 0xffff0000),
InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%))", 10, 20, 30, 0x90641e2d), InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%))", 10, 20, 30, 0xe6641e2d),
InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%) ) ", 10, 20, 30, 0x90641e2d), InlineData("drop-shadow(10 20 30 rgba(100, 30, 45, 90%) ) ", 10, 20, 30, 0xe6641e2d)
] ]
public void Parse_Parses_DropShadow(string s, double x, double y, double r, uint color) public void Parse_Parses_DropShadow(string s, double x, double y, double r, uint color)
{ {
@ -36,6 +35,7 @@ public class EffectTests
Assert.Equal(y, effect.OffsetY); Assert.Equal(y, effect.OffsetY);
Assert.Equal(r, effect.BlurRadius); Assert.Equal(r, effect.BlurRadius);
Assert.Equal(1, effect.Opacity); Assert.Equal(1, effect.Opacity);
Assert.Equal(color, effect.Color.ToUInt32());
} }
[Theory, [Theory,
@ -44,7 +44,7 @@ public class EffectTests
InlineData("blur()"), InlineData("blur()"),
InlineData("blur(123"), InlineData("blur(123"),
InlineData("blur(aaab)"), InlineData("blur(aaab)"),
InlineData("drop-shadow(-10 -20 -30)"), InlineData("drop-shadow(-10 -20 -30)")
] ]
public void Invalid_Effect_Parse_Fails(string b) public void Invalid_Effect_Parse_Fails(string b)
{ {
@ -58,10 +58,7 @@ public class EffectTests
InlineData("drop-shadow(10 15 5)", 0, 0, 16, 21), InlineData("drop-shadow(10 15 5)", 0, 0, 16, 21),
InlineData("drop-shadow(0 0 5)", 6, 6, 6, 6), InlineData("drop-shadow(0 0 5)", 6, 6, 6, 6),
InlineData("drop-shadow(3 3 5)", 3, 3, 9, 9) InlineData("drop-shadow(3 3 5)", 3, 3, 9, 9)
] ]
public static void PaddingIsCorrectlyCalculated(string effect, double left, double top, double right, double bottom) public static void PaddingIsCorrectlyCalculated(string effect, double left, double top, double right, double bottom)
{ {
var padding = Effect.Parse(effect).GetEffectOutputPadding(); var padding = Effect.Parse(effect).GetEffectOutputPadding();
@ -70,4 +67,4 @@ public class EffectTests
Assert.Equal(right, padding.Right); Assert.Equal(right, padding.Right);
Assert.Equal(bottom, padding.Bottom); Assert.Equal(bottom, padding.Bottom);
} }
} }

6
tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs

@ -525,12 +525,12 @@ namespace Avalonia.Base.UnitTests.Styling
private class TestConverter : IValueConverter private class TestConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
return value.ToString() + "bar"; return value + "bar";
} }
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

53
tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs

@ -1,11 +1,7 @@
using System; using System.Linq;
using System.Linq;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Automation.Provider;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Xunit; using Xunit;
@ -41,7 +37,7 @@ namespace Avalonia.Controls.UnitTests.Automation
{ {
var contentControl = new ContentControl var contentControl = new ContentControl
{ {
Template = new FuncControlTemplate<ContentControl>((o, ns) => Template = new FuncControlTemplate<ContentControl>((o, _) =>
new ContentPresenter new ContentPresenter
{ {
Name = "PART_ContentPresenter", Name = "PART_ContentPresenter",
@ -180,50 +176,5 @@ namespace Avalonia.Controls.UnitTests.Automation
{ {
return ControlAutomationPeer.CreatePeerForElement(control); return ControlAutomationPeer.CreatePeerForElement(control);
} }
private class TestControl : Control
{
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TestAutomationPeer(this);
}
}
private class AutomationTestRoot : TestRoot
{
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TestRootAutomationPeer(this);
}
}
private class TestAutomationPeer : ControlAutomationPeer
{
public TestAutomationPeer( Control owner)
: base(owner)
{
}
}
private class TestRootAutomationPeer : ControlAutomationPeer, IRootProvider
{
public TestRootAutomationPeer(Control owner)
: base(owner)
{
}
public ITopLevelImpl PlatformImpl => throw new System.NotImplementedException();
public event EventHandler? FocusChanged;
public AutomationPeer GetFocus()
{
throw new System.NotImplementedException();
}
public AutomationPeer GetPeerFromPoint(Point p)
{
throw new System.NotImplementedException();
}
}
} }
} }

2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -2424,7 +2424,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
set => base.SelectionMode = value; set => base.SelectionMode = value;
} }
public new bool MoveSelection(NavigationDirection direction, bool wrap) public bool MoveSelection(NavigationDirection direction, bool wrap)
{ {
return base.MoveSelection(direction, wrap); return base.MoveSelection(direction, wrap);
} }

3
tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs

@ -1,8 +1,9 @@
using Avalonia.Data; using Avalonia.Data;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Xunit; using Xunit;
#pragma warning disable CS0618 // Type or member is obsolete -- we're testing these members
namespace Avalonia.Controls.Primitives.UnitTests namespace Avalonia.Controls.Primitives.UnitTests
{ {
public class ToggleButtonTests public class ToggleButtonTests

12
tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs

@ -1,4 +1,3 @@
using System;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Markup.Xaml.Converters; using Avalonia.Markup.Xaml.Converters;
using Avalonia.Markup.Xaml.XamlIl.Runtime; using Avalonia.Markup.Xaml.XamlIl.Runtime;
@ -13,8 +12,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
public AvaloniaPropertyConverterTest() public AvaloniaPropertyConverterTest()
{ {
// Ensure properties are registered. // Ensure properties are registered.
var foo = Class1.FooProperty; _ = Class1.FooProperty;
var attached = AttachedOwner.AttachedProperty; _ = AttachedOwner.AttachedProperty;
} }
[Fact] [Fact]
@ -114,13 +113,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
{ {
public static readonly StyledProperty<string> FooProperty = public static readonly StyledProperty<string> FooProperty =
AvaloniaProperty.Register<Class1, string>("Foo"); AvaloniaProperty.Register<Class1, string>("Foo");
public ThemeVariant ThemeVariant
{
get => throw new NotImplementedException();
}
public event EventHandler ThemeVariantChanged;
} }
private class AttachedOwner private class AttachedOwner

224
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -48,7 +50,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -74,7 +76,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StringProperty}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -100,7 +102,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding Path=StringProperty}' Name='textBlock' /> <TextBlock Text='{CompiledBinding Path=StringProperty}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -132,7 +134,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</ItemsControl> </ItemsControl>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<ItemsControl>("itemsControl"); var textBlock = window.GetControl<ItemsControl>("itemsControl");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -162,7 +164,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StaticProperty}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StaticProperty}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
textBlock.DataContext = new TestDataContext(); textBlock.DataContext = new TestDataContext();
Assert.Equal(TestDataContext.StaticProperty, textBlock.Text); Assert.Equal(TestDataContext.StaticProperty, textBlock.Text);
@ -181,7 +183,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StringProperty, DataType=local:TestDataContext}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StringProperty, DataType=local:TestDataContext}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -206,7 +208,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StringProperty, DataType={x:Type local:TestDataContext}}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StringProperty, DataType={x:Type local:TestDataContext}}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -232,7 +234,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding TaskProperty^}' Name='textBlock' /> <TextBlock Text='{CompiledBinding TaskProperty^}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -258,7 +260,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding ObservableProperty^}' Name='textBlock' /> <TextBlock Text='{CompiledBinding ObservableProperty^}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
DelayedBinding.ApplyBindings(textBlock); DelayedBinding.ApplyBindings(textBlock);
@ -289,7 +291,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding ListProperty[3]}' Name='textBlock' /> <TextBlock Text='{CompiledBinding ListProperty[3]}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -315,7 +317,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding ArrayProperty[3]}' Name='textBlock' /> <TextBlock Text='{CompiledBinding ArrayProperty[3]}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -341,7 +343,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding ObservableCollectionProperty[3]}' Name='textBlock' /> <TextBlock Text='{CompiledBinding ObservableCollectionProperty[3]}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -371,10 +373,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock DataContext='{CompiledBinding StringProperty}' Text='{CompiledBinding}' Name='textBlock' /> <TextBlock DataContext='{CompiledBinding StringProperty}' Text='{CompiledBinding}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -400,7 +402,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding NonIntegerIndexerProperty[Test]}' Name='textBlock' /> <TextBlock Text='{CompiledBinding NonIntegerIndexerProperty[Test]}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -429,7 +431,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding NonIntegerIndexerInterfaceProperty[Test]}' Name='textBlock' /> <TextBlock Text='{CompiledBinding NonIntegerIndexerInterfaceProperty[Test]}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -463,7 +465,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Name='target' Content='{CompiledBinding StringProperty}' /> <ContentControl Name='target' Content='{CompiledBinding StringProperty}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target"); var target = window.GetControl<ContentControl>("target");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -473,9 +475,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate(); window.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.UpdateChild(); target.Presenter!.UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
} }
} }
@ -560,7 +562,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</ItemsControl> </ItemsControl>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ItemsControl>("target"); var target = window.GetControl<ItemsControl>("target");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -570,9 +572,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate(); window.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter!.ApplyTemplate();
Assert.Equal(dataContext.ListProperty[0], (string)((ContentPresenter)target.Presenter.Panel.Children[0]).Content); Assert.Equal(dataContext.ListProperty[0], (string?)((ContentPresenter)target.Presenter.Panel!.Children[0]).Content);
} }
} }
@ -598,8 +600,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</local:DataGridLikeControl.Columns> </local:DataGridLikeControl.Columns>
</local:DataGridLikeControl> </local:DataGridLikeControl>
</Window>"); </Window>");
var target = window.FindControl<DataGridLikeControl>("target"); var target = window.GetControl<DataGridLikeControl>("target");
var column = target!.Columns.Single(); var column = target.Columns.Single();
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -611,13 +613,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
target.ApplyTemplate(); target.ApplyTemplate();
// Assert DataGridLikeColumn.Binding data type. // Assert DataGridLikeColumn.Binding data type.
var compiledPath = ((CompiledBindingExtension)column.Binding).Path; var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements)); var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(int), node.Property.PropertyType); Assert.Equal(typeof(int), node.Property.PropertyType);
// Assert DataGridLikeColumn.Template data type by evaluating the template. // Assert DataGridLikeColumn.Template data type by evaluating the template.
var firstItem = dataContext.ListProperty[0]; var firstItem = dataContext.ListProperty[0];
var textBlockFromTemplate = (TextBlock)column.Template.Build(firstItem); var textBlockFromTemplate = (TextBlock)column.Template!.Build(firstItem)!;
textBlockFromTemplate.DataContext = firstItem; textBlockFromTemplate.DataContext = firstItem;
Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text); Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text);
} }
@ -645,8 +647,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</local:DataGridLikeControl.Columns> </local:DataGridLikeControl.Columns>
</local:DataGridLikeControl> </local:DataGridLikeControl>
</Window>"); </Window>");
var target = window.FindControl<DataGridLikeControl>("target"); var target = window.GetControl<DataGridLikeControl>("target");
var column = target!.Columns.Single(); var column = target.Columns.Single();
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
dataContext.ListProperty.Add("Test"); dataContext.ListProperty.Add("Test");
@ -656,13 +658,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
target.ApplyTemplate(); target.ApplyTemplate();
// Assert DataGridLikeColumn.Binding data type. // Assert DataGridLikeColumn.Binding data type.
var compiledPath = ((CompiledBindingExtension)column.Binding).Path; var compiledPath = ((CompiledBindingExtension)column.Binding!).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements)); var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(int), node.Property.PropertyType); Assert.Equal(typeof(int), node.Property.PropertyType);
// Assert DataGridLikeColumn.Template data type by evaluating the template. // Assert DataGridLikeColumn.Template data type by evaluating the template.
var firstItem = dataContext.ListProperty[0]; var firstItem = dataContext.ListProperty[0];
var textBlockFromTemplate = (TextBlock)column.Template.Build(firstItem); var textBlockFromTemplate = (TextBlock)column.Template!.Build(firstItem)!;
textBlockFromTemplate.DataContext = firstItem; textBlockFromTemplate.DataContext = firstItem;
Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text); Assert.Equal(firstItem.Length.ToString(), textBlockFromTemplate.Text);
} }
@ -707,7 +709,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' /> <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target"); var target = window.GetControl<ContentControl>("target");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -717,9 +719,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate(); window.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.UpdateChild(); target.Presenter!.UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
} }
} }
@ -740,7 +742,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' /> <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target"); var target = window.GetControl<ContentControl>("target");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -750,9 +752,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate(); window.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.UpdateChild(); target.Presenter!.UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
} }
} }
@ -773,7 +775,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' /> <ContentControl x:DataType='local:TestDataContext' Name='target' Content='{CompiledBinding}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<ContentControl>("target"); var target = window.GetControl<ContentControl>("target");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -783,9 +785,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
window.ApplyTemplate(); window.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.UpdateChild(); target.Presenter!.UpdateChild();
Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child!).Text);
} }
} }
@ -805,7 +807,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</StackPanel> </StackPanel>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("text2"); var textBlock = window.GetControl<TextBlock>("text2");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -834,7 +836,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</StackPanel> </StackPanel>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("text2"); var textBlock = window.GetControl<TextBlock>("text2");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -861,10 +863,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType=Window}}' x:Name='text'/> <TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType=Window}}' x:Name='text'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<TextBlock>("text"); var target = window.GetControl<TextBlock>("text");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
Assert.Equal("test", target.Text); Assert.Equal("test", target.Text);
@ -885,10 +887,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}' x:Name='text'/> <TextBlock Text='{CompiledBinding Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}' x:Name='text'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<TextBlock>("text"); var target = window.GetControl<TextBlock>("text");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
Assert.Equal("test", target.Text); Assert.Equal("test", target.Text);
@ -1003,10 +1005,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding Length, Source=Test}' x:Name='text'/> <TextBlock Text='{CompiledBinding Length, Source=Test}' x:Name='text'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var target = window.FindControl<TextBlock>("text"); var target = window.GetControl<TextBlock>("text");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
target.ApplyTemplate(); target.ApplyTemplate();
Assert.Equal("Test".Length.ToString(), target.Text); Assert.Equal("Test".Length.ToString(), target.Text);
@ -1030,7 +1032,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text); Assert.Equal("foobar", textBlock.Text);
} }
@ -1054,7 +1056,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text); Assert.Equal("foobar", textBlock.Text);
} }
@ -1079,7 +1081,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text); Assert.Equal("foobar", textBlock.Text);
} }
@ -1105,7 +1107,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
Assert.Equal("foobar", textBlock.Text); Assert.Equal("foobar", textBlock.Text);
} }
@ -1125,7 +1127,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
Assert.Equal(Brushes.Red.Color, contentControl.Content); Assert.Equal(Brushes.Red.Color, contentControl.Content);
} }
@ -1145,7 +1147,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{Binding StringProperty}' Name='textBlock' /> <TextBlock Text='{Binding StringProperty}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1188,7 +1190,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.Title}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.Title}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
Assert.Equal("foo", contentControl.Content); Assert.Equal("foo", contentControl.Content);
} }
@ -1208,7 +1210,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
window.DataContext = new TestDataContext() { StringProperty = "Foo" }; window.DataContext = new TestDataContext() { StringProperty = "Foo" };
@ -1230,7 +1232,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
window.DataContext = new TestDataContext() { StringProperty = "Foo" }; window.DataContext = new TestDataContext() { StringProperty = "Foo" };
@ -1267,7 +1269,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
@ -1290,7 +1292,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext)}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var dataContext = "foo"; var dataContext = "foo";
@ -1313,7 +1315,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1339,7 +1341,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.DataContext(local:TestDataContext).StringProperty}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.DataContext(local:TestDataContext).StringProperty}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1365,7 +1367,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding ((local:TestData)ObjectsArrayProperty[0]).StringProperty}' Name='contentControl' /> <ContentControl Content='{CompiledBinding ((local:TestData)ObjectsArrayProperty[0]).StringProperty}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var data = new TestData() var data = new TestData()
{ {
@ -1395,7 +1397,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' /> <ContentControl Content='{CompiledBinding $parent.((local:TestDataContext)DataContext).StringProperty}' Name='contentControl' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var contentControl = window.FindControl<ContentControl>("contentControl"); var contentControl = window.GetControl<ContentControl>("contentControl");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1425,7 +1427,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding}' Name='textBlock' /> <TextBlock Text='{CompiledBinding}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1451,7 +1453,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding StringFormat=bar-\{0\}}' Name='textBlock' /> <TextBlock Text='{CompiledBinding StringFormat=bar-\{0\}}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1477,7 +1479,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding .}' Name='textBlock' /> <TextBlock Text='{CompiledBinding .}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1503,7 +1505,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Text='{CompiledBinding Path=., StringFormat=bar-\{0\}}' Name='textBlock' /> <TextBlock Text='{CompiledBinding Path=., StringFormat=bar-\{0\}}' Name='textBlock' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext var dataContext = new TestDataContext
{ {
@ -1550,10 +1552,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Name='textBlock' Text='{CompiledBinding $self.Name}' /> <TextBlock Name='textBlock' Text='{CompiledBinding $self.Name}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
Assert.Equal(textBlock.Name, textBlock.Text); Assert.Equal(textBlock.Name, textBlock.Text);
} }
@ -1577,10 +1579,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<Button Name='button' /> <Button Name='button' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button"); var button = window.GetControl<Button>("button");
window.ApplyTemplate(); window.ApplyTemplate();
window.Presenter.ApplyTemplate(); window.Presenter!.ApplyTemplate();
Assert.True(button.IsVisible); Assert.True(button.IsVisible);
@ -1612,12 +1614,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
window.DataContext = new MethodDataContext(); window.DataContext = new MethodDataContext();
Assert.IsAssignableFrom(typeof(Action), window.FindControl<ContentControl>("action").Content); Assert.IsAssignableFrom(typeof(Action), window.GetControl<ContentControl>("action").Content);
Assert.IsAssignableFrom(typeof(Func<int>), window.FindControl<ContentControl>("func").Content); Assert.IsAssignableFrom(typeof(Func<int>), window.GetControl<ContentControl>("func").Content);
Assert.IsAssignableFrom(typeof(Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.FindControl<ContentControl>("action16").Content); Assert.IsAssignableFrom(typeof(Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.GetControl<ContentControl>("action16").Content);
Assert.IsAssignableFrom(typeof(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.FindControl<ContentControl>("func16").Content); Assert.IsAssignableFrom(typeof(Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>), window.GetControl<ContentControl>("func16").Content);
Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl<ContentControl>("customvoid").Content.GetType())); Assert.True(typeof(Delegate).IsAssignableFrom(window.GetControl<ContentControl>("customvoid").Content!.GetType()));
Assert.True(typeof(Delegate).IsAssignableFrom(window.FindControl<ContentControl>("customint").Content.GetType())); Assert.True(typeof(Delegate).IsAssignableFrom(window.GetControl<ContentControl>("customint").Content!.GetType()));
} }
} }
@ -1634,7 +1636,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<Button Name='button' Command='{CompiledBinding Method}'/> <Button Name='button' Command='{CompiledBinding Method}'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button"); var button = window.GetControl<Button>("button");
var vm = new MethodAsCommandDataContext(); var vm = new MethodAsCommandDataContext();
button.DataContext = vm; button.DataContext = vm;
@ -1659,7 +1661,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<Button Name='button' Command='{CompiledBinding Method1}' CommandParameter='5'/> <Button Name='button' Command='{CompiledBinding Method1}' CommandParameter='5'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button"); var button = window.GetControl<Button>("button");
var vm = new MethodAsCommandDataContext(); var vm = new MethodAsCommandDataContext();
button.DataContext = vm; button.DataContext = vm;
@ -1684,7 +1686,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Name='textBlock' Text='{CompiledBinding Method}'/> <TextBlock Name='textBlock' Text='{CompiledBinding Method}'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var vm = new MethodAsCommandDataContext(); var vm = new MethodAsCommandDataContext();
textBlock.DataContext = vm; textBlock.DataContext = vm;
@ -1710,7 +1712,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<Button Name='button' Command='{CompiledBinding Do}' CommandParameter='{CompiledBinding Parameter, Mode=OneTime}'/> <Button Name='button' Command='{CompiledBinding Do}' CommandParameter='{CompiledBinding Parameter, Mode=OneTime}'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button"); var button = window.GetControl<Button>("button");
var vm = new MethodAsCommandDataContext() var vm = new MethodAsCommandDataContext()
{ {
Parameter = commandParameter Parameter = commandParameter
@ -1738,7 +1740,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<Button Name='button' Command='{CompiledBinding Do}'/> <Button Name='button' Command='{CompiledBinding Do}'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl<Button>("button"); var button = window.GetControl<Button>("button");
var vm = new MethodAsCommandDataContext() var vm = new MethodAsCommandDataContext()
{ {
Parameter = null, Parameter = null,
@ -1770,7 +1772,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
x:DataType='local:TestDataContext' x:DataType='local:TestDataContext'
X='{CompiledBinding StringProperty}' />"; X='{CompiledBinding StringProperty}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml); var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var compiledPath = ((CompiledBindingExtension)control.X).Path; var compiledPath = ((CompiledBindingExtension)control.X!).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements)); var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType); Assert.Equal(typeof(string), node.Property.PropertyType);
@ -1788,7 +1790,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests' xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />"; X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml); var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var compiledPath = ((CompiledBindingExtension)control.X).Path; var compiledPath = ((CompiledBindingExtension)control.X!).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements)); var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType); Assert.Equal(typeof(string), node.Property.PropertyType);
@ -1807,7 +1809,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />"; X='{CompiledBinding StringProperty, DataType=local:TestDataContext}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml), var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml),
new RuntimeXamlLoaderConfiguration { UseCompiledBindingsByDefault = true }); new RuntimeXamlLoaderConfiguration { UseCompiledBindingsByDefault = true });
var compiledPath = ((CompiledBindingExtension)control.X).Path; var compiledPath = ((CompiledBindingExtension)control.X!).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements)); var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType); Assert.Equal(typeof(string), node.Property.PropertyType);
@ -1830,7 +1832,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<ComboBox x:Name='comboBox' ItemsSource='{Binding GenericProperty}' SelectedItem='{Binding GenericProperty.CurrentItem}' /> <ComboBox x:Name='comboBox' ItemsSource='{Binding GenericProperty}' SelectedItem='{Binding GenericProperty.CurrentItem}' />
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var comboBox = window.FindControl<ComboBox>("comboBox"); var comboBox = window.GetControl<ComboBox>("comboBox");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
dataContext.GenericProperty.Add(123); dataContext.GenericProperty.Add(123);
@ -1857,7 +1859,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
<TextBlock Name='textBlock' Text='{{Binding DecimalValue, StringFormat=c2}}'/> <TextBlock Name='textBlock' Text='{{Binding DecimalValue, StringFormat=c2}}'/>
</Window>"; </Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock"); var textBlock = window.GetControl<TextBlock>("textBlock");
var dataContext = new TestDataContext(); var dataContext = new TestDataContext();
window.DataContext = dataContext; window.DataContext = dataContext;
@ -1887,7 +1889,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public interface IHasProperty public interface IHasProperty
{ {
string StringProperty { get; set; } string? StringProperty { get; set; }
} }
public interface IHasPropertyDerived : IHasProperty public interface IHasPropertyDerived : IHasProperty
@ -1902,34 +1904,34 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{ {
public static IValueConverter Instance { get; } = new AppendConverter(); public static IValueConverter Instance { get; } = new AppendConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> string.Format("{0}+{1}+{2}", value, parameter, culture); => string.Format("{0}+{1}+{2}", value, parameter, culture);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException(); => throw new NotImplementedException();
} }
public class TestData public class TestData
{ {
public string StringProperty { get; set; } public string? StringProperty { get; set; }
} }
public class TestDataContextBaseClass {} public class TestDataContextBaseClass {}
public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived, IHasExplicitProperty public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived, IHasExplicitProperty
{ {
public string StringProperty { get; set; } public string? StringProperty { get; set; }
public Task<string> TaskProperty { get; set; } public Task<string>? TaskProperty { get; set; }
public IObservable<string> ObservableProperty { get; set; } public IObservable<string>? ObservableProperty { get; set; }
public ObservableCollection<string> ObservableCollectionProperty { get; set; } = new ObservableCollection<string>(); public ObservableCollection<string> ObservableCollectionProperty { get; set; } = new ObservableCollection<string>();
public string[] ArrayProperty { get; set; } public string[]? ArrayProperty { get; set; }
public object[] ObjectsArrayProperty { get; set; } public object[]? ObjectsArrayProperty { get; set; }
public List<string> ListProperty { get; set; } = new List<string>(); public List<string> ListProperty { get; set; } = new List<string>();
@ -1966,7 +1968,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public class ListItemCollectionView<T> : List<T> public class ListItemCollectionView<T> : List<T>
{ {
public T CurrentItem { get; set; } public T? CurrentItem { get; set; }
} }
public class MethodDataContext public class MethodDataContext
@ -1983,15 +1985,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public class MethodAsCommandDataContext : INotifyPropertyChanged public class MethodAsCommandDataContext : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public string Method() => Value = "Called"; public string Method() => Value = "Called";
public string Method1(int i) => Value = $"Called {i}"; public string Method1(int i) => Value = $"Called {i}";
public string Method2(int i, int j) => Value = $"Called {i},{j}"; public string Method2(int i, int j) => Value = $"Called {i},{j}";
public string Value { get; private set; } = "Not called"; public string Value { get; private set; } = "Not called";
object _parameter; private object? _parameter;
public object Parameter
public object? Parameter
{ {
get => _parameter; get => _parameter;
set set
@ -2010,7 +2013,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Value = $"Do {parameter}"; Value = $"Do {parameter}";
} }
[Metadata.DependsOn(nameof(Parameter))] [DependsOn(nameof(Parameter))]
public bool CanDo(object parameter) public bool CanDo(object parameter)
{ {
return ReferenceEquals(null, Parameter) == false; return ReferenceEquals(null, Parameter) == false;
@ -2020,22 +2023,22 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public class CustomDataTemplate : IDataTemplate public class CustomDataTemplate : IDataTemplate
{ {
[DataType] [DataType]
public Type FancyDataType { get; set; } public Type? FancyDataType { get; set; }
[Content] [Content]
[TemplateContent] [TemplateContent]
public object Content { get; set; } public object? Content { get; set; }
public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true; public bool Match(object? data) => FancyDataType?.IsInstanceOfType(data) ?? true;
public Control Build(object data) => TemplateContent.Load(Content)?.Result; public Control? Build(object? data) => TemplateContent.Load(Content)?.Result;
} }
public class CustomDataTemplateInherit : CustomDataTemplate { } public class CustomDataTemplateInherit : CustomDataTemplate { }
public class AssignBindingControl : Control public class AssignBindingControl : Control
{ {
[AssignBinding] public IBinding X { get; set; } [AssignBinding] public IBinding? X { get; set; }
} }
public class DataGridLikeControl : Control public class DataGridLikeControl : Control
@ -2046,8 +2049,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
x => x.Items, x => x.Items,
(x, v) => x.Items = v); (x, v) => x.Items = v);
private IEnumerable _items; private IEnumerable? _items;
public IEnumerable Items
public IEnumerable? Items
{ {
get => _items; get => _items;
set => SetAndRaise(ItemsProperty, ref _items, value); set => SetAndRaise(ItemsProperty, ref _items, value);
@ -2060,9 +2064,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{ {
[AssignBinding] [AssignBinding]
[InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))] [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
public IBinding Binding { get; set; } public IBinding? Binding { get; set; }
[InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))] [InheritDataTypeFromItems(nameof(DataGridLikeControl.Items), AncestorType = typeof(DataGridLikeControl))]
public IDataTemplate Template { get; set; } public IDataTemplate? Template { get; set; }
} }
} }

19
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs

@ -1,8 +1,9 @@
using System; #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Xunit; using Xunit;
@ -40,9 +41,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{ {
var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents); var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
var userControl = Assert.IsType<UserControl>(compiled[1]); var userControl = Assert.IsType<UserControl>(compiled[1]);
var border = userControl.FindControl<Border>("border"); var border = userControl.GetControl<Border>("border");
var brush = (ISolidColorBrush)border.Background; var brush = (ISolidColorBrush)border.Background!;
Assert.Equal(0xff506070, brush.Color.ToUInt32()); Assert.Equal(0xff506070, brush.Color.ToUInt32());
} }
} }
@ -130,17 +131,21 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{ {
private readonly Dictionary<object, IResourceProvider> _langs = new(); private readonly Dictionary<object, IResourceProvider> _langs = new();
public IResourceHost Owner { get; private set; } public IResourceHost? Owner { get; private set; }
public bool HasResources => true; public bool HasResources => true;
public event EventHandler OwnerChanged; public event EventHandler? OwnerChanged
{
add { }
remove { }
}
public void AddOwner(IResourceHost owner) => Owner = owner; public void AddOwner(IResourceHost owner) => Owner = owner;
public void RemoveOwner(IResourceHost owner) => Owner = null; public void RemoveOwner(IResourceHost owner) => Owner = null;
public bool TryGetResource(object key, ThemeVariant theme, out object? value) public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{ {
if (_langs.TryGetValue("English", out var res)) if (_langs.TryGetValue("English", out var res))
{ {

14
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -1,16 +1,16 @@
#nullable enable
using System; using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Controls.Shapes;
using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Xunit; using Xunit;
using Path = System.IO.Path; using Path = System.IO.Path;
#pragma warning disable CS0649 #pragma warning disable CS0649
#if AVALONIA_SKIA #if AVALONIA_SKIA
@ -69,7 +69,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
var fmt = new PixelFormat(fmte); var fmt = new PixelFormat(fmte);
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
var fb = new Framebuffer(fmt, new PixelSize(80, 80)); var fb = new Framebuffer(fmt, new PixelSize(80, 80));
var r = Avalonia.AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>(); var r = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
using(var cpuContext = r.CreateBackendContext(null)) using(var cpuContext = r.CreateBackendContext(null))
using (var target = cpuContext.CreateRenderTarget(new object[] { fb })) using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))
using (var ctx = target.CreateDrawingContext()) using (var ctx = target.CreateDrawingContext())
@ -94,7 +94,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
var rc = new Rect(0, 0, 60, 60); var rc = new Rect(0, 0, 60, 60);
ctx.DrawBitmap(bmp.PlatformImpl, 1, rc, rc); ctx.DrawBitmap(bmp.PlatformImpl, 1, rc, rc);
} }
rtb.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png")); rtb.Save(Path.Combine(OutputPath, testName + ".out.png"));
} }
CompareImagesNoRenderer(testName); CompareImagesNoRenderer(testName);
} }
@ -123,7 +123,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
var name = nameof(WriteableBitmapShouldBeUsable) + "_" + fmt; var name = nameof(WriteableBitmapShouldBeUsable) + "_" + fmt;
writeableBitmap.Save(System.IO.Path.Combine(OutputPath, name + ".out.png")); writeableBitmap.Save(Path.Combine(OutputPath, name + ".out.png"));
CompareImagesNoRenderer(name); CompareImagesNoRenderer(name);
} }
@ -177,7 +177,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
var testName = nameof(BitmapsShouldSupportTranscoders_Lenna) + "_" + formatName + names[step]; var testName = nameof(BitmapsShouldSupportTranscoders_Lenna) + "_" + formatName + names[step];
var path = System.IO.Path.Combine(OutputPath, testName + ".out.png"); var path = Path.Combine(OutputPath, testName + ".out.png");
fixed (byte* pData = data) fixed (byte* pData = data)
{ {
Bitmap? b = null; Bitmap? b = null;

2
tests/Avalonia.RenderTests/Media/TileBrushTests.cs

@ -44,7 +44,6 @@ public class DrawingBrushTests: TestBase
#if AVALONIA_SKIA #if AVALONIA_SKIA
[Fact] [Fact]
#endif
public async Task DrawingBrushIsProperlyUpscaled() public async Task DrawingBrushIsProperlyUpscaled()
{ {
Decorator target = new Decorator Decorator target = new Decorator
@ -66,6 +65,7 @@ public class DrawingBrushTests: TestBase
await RenderToFile(target); await RenderToFile(target);
CompareImages(); CompareImages();
} }
#endif
GeometryDrawing CreateDrawing() GeometryDrawing CreateDrawing()
{ {

4
tests/Avalonia.RenderTests/TestBase.cs

@ -83,7 +83,7 @@ namespace Avalonia.Direct2D1.RenderTests
get; get;
} }
protected async Task RenderToFile(Control target, [CallerMemberName] string testName = "", double dpi = 96) protected Task RenderToFile(Control target, [CallerMemberName] string testName = "", double dpi = 96)
{ {
if (!Directory.Exists(OutputPath)) if (!Directory.Exists(OutputPath))
{ {
@ -124,6 +124,8 @@ namespace Avalonia.Direct2D1.RenderTests
} }
writableBitmap.Save(compositedPath); writableBitmap.Save(compositedPath);
} }
return Task.CompletedTask;
} }
class BitmapFramebufferSurface : IFramebufferPlatformSurface class BitmapFramebufferSurface : IFramebufferPlatformSurface

89
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -1,8 +1,9 @@
using System; #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -30,10 +31,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Single(textLine.TextRuns); Assert.Single(textLine.TextRuns);
var textRun = textLine.TextRuns[0]; var textRun = textLine.TextRuns[0];
Assert.NotNull(textRun.Properties);
Assert.Equal(defaultProperties.Typeface, textRun.Properties.Typeface); Assert.Equal(defaultProperties.Typeface, textRun.Properties.Typeface);
Assert.Equal(defaultProperties.ForegroundBrush, textRun.Properties.ForegroundBrush); Assert.Equal(defaultProperties.ForegroundBrush, textRun.Properties.ForegroundBrush);
@ -57,6 +62,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(5, textLine.TextRuns.Count); Assert.Equal(5, textLine.TextRuns.Count);
Assert.Equal(50, textLine.Length); Assert.Equal(50, textLine.Length);
@ -120,6 +127,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(5, textLine.TextRuns.Count); Assert.Equal(5, textLine.TextRuns.Count);
Assert.Equal(14, textLine.Length); Assert.Equal(14, textLine.Length);
@ -153,6 +162,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(text.Length, textLine.Length); Assert.Equal(text.Length, textLine.Length);
for (var i = 0; i < GenericTextRunPropertiesRuns.Length; i++) for (var i = 0; i < GenericTextRunPropertiesRuns.Length; i++)
@ -186,6 +197,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(numberOfRuns, textLine.TextRuns.Count); Assert.Equal(numberOfRuns, textLine.TextRuns.Count);
} }
} }
@ -207,6 +220,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(1, textLine.TextRuns.Count); Assert.Equal(1, textLine.TextRuns.Count);
} }
} }
@ -228,6 +243,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var firstRun = textLine.TextRuns[0]; var firstRun = textLine.TextRuns[0];
Assert.Equal(4, firstRun.Length); Assert.Equal(4, firstRun.Length);
@ -249,7 +266,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var formatter = new TextFormatterImpl(); var formatter = new TextFormatterImpl();
var line = formatter.FormatLine(textSource, 0, 33, paragraphProperties); formatter.FormatLine(textSource, 0, 33, paragraphProperties);
textSource = new SingleBufferTextSource(text, defaultProperties); textSource = new SingleBufferTextSource(text, defaultProperties);
@ -262,6 +279,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, currentPosition, 1, paragraphProperties); formatter.FormatLine(textSource, currentPosition, 1, paragraphProperties);
Assert.NotNull(textLine);
if (text.Length - currentPosition > expectedCharactersPerLine) if (text.Length - currentPosition > expectedCharactersPerLine)
{ {
Assert.Equal(expectedCharactersPerLine, textLine.Length); Assert.Equal(expectedCharactersPerLine, textLine.Length);
@ -318,6 +337,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, currentPosition, paragraphWidth, formatter.FormatLine(textSource, currentPosition, paragraphWidth,
new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap)); new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap));
Assert.NotNull(textLine);
var end = textLine.FirstTextSourceIndex + textLine.Length - 1; var end = textLine.FirstTextSourceIndex + textLine.Length - 1;
Assert.True(expected.Contains(end)); Assert.True(expected.Contains(end));
@ -351,6 +372,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties, lineHeight: 50)); new GenericTextParagraphProperties(defaultProperties, lineHeight: 50));
Assert.NotNull(textLine);
Assert.Equal(50, textLine.Height); Assert.Equal(50, textLine.Height);
} }
} }
@ -381,6 +404,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, textSourceIndex, 200, paragraphProperties); formatter.FormatLine(textSource, textSourceIndex, 200, paragraphProperties);
Assert.NotNull(textLine);
Assert.True(textLine.Width <= 200); Assert.True(textLine.Width <= 200);
textSourceIndex += textLine.Length; textSourceIndex += textLine.Length;
@ -407,6 +432,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, textSourceIndex, 3, paragraphProperties); formatter.FormatLine(textSource, textSourceIndex, 3, paragraphProperties);
Assert.NotNull(textLine);
Assert.NotEqual(0, textLine.Length); Assert.NotEqual(0, textLine.Length);
textSourceIndex += textLine.Length; textSourceIndex += textLine.Length;
@ -454,6 +481,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, currentPosition, 300, formatter.FormatLine(textSource, currentPosition, 300,
new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow)); new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
Assert.NotNull(textLine);
currentPosition += textLine.Length; currentPosition += textLine.Length;
if (textLine.Width > 300 || currentHeight + textLine.Height > 240) if (textLine.Width > 300 || currentHeight + textLine.Height > 240)
@ -502,6 +531,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, 0, 100, paragraphProperties); formatter.FormatLine(textSource, 0, 100, paragraphProperties);
Assert.NotNull(textLine);
var expectedOffset = 0d; var expectedOffset = 0d;
switch (textAlignment) switch (textAlignment)
@ -534,13 +565,15 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var formatter = new TextFormatterImpl(); var formatter = new TextFormatterImpl();
var textPosition = 87; var textPosition = 87;
TextLineBreak lastBreak = null; TextLineBreak? lastBreak = null;
while (textPosition < text.Length) while (textPosition < text.Length)
{ {
var textLine = var textLine =
formatter.FormatLine(textSource, textPosition, 50, paragraphProperties, lastBreak); formatter.FormatLine(textSource, textPosition, 50, paragraphProperties, lastBreak);
Assert.NotNull(textLine);
Assert.Equal(textLine.Length, textLine.TextRuns.Sum(x => x.Length)); Assert.Equal(textLine.Length, textLine.TextRuns.Sum(x => x.Length));
textPosition += textLine.Length; textPosition += textLine.Length;
@ -564,6 +597,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, 0, 33, paragraphProperties); formatter.FormatLine(textSource, 0, 33, paragraphProperties);
Assert.NotNull(textLine);
var remainingRunsLineBreak = Assert.IsType<WrappingTextLineBreak>(textLine.TextLineBreak); var remainingRunsLineBreak = Assert.IsType<WrappingTextLineBreak>(textLine.TextLineBreak);
var remainingRuns = remainingRunsLineBreak.AcquireRemainingRuns(); var remainingRuns = remainingRunsLineBreak.AcquireRemainingRuns();
Assert.NotNull(remainingRuns); Assert.NotNull(remainingRuns);
@ -592,6 +627,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var expectedTextLine = formatter.FormatLine(new SingleBufferTextSource(text, defaultProperties), var expectedTextLine = formatter.FormatLine(new SingleBufferTextSource(text, defaultProperties),
0, double.PositiveInfinity, paragraphProperties); 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(expectedTextLine);
var expectedRuns = expectedTextLine.TextRuns.Cast<ShapedTextRun>().ToList(); var expectedRuns = expectedTextLine.TextRuns.Cast<ShapedTextRun>().ToList();
var expectedGlyphs = expectedRuns var expectedGlyphs = expectedRuns
@ -613,6 +650,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); formatter.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
var shapedRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList(); var shapedRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList();
var actualGlyphs = shapedRuns var actualGlyphs = shapedRuns
@ -637,6 +676,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
Assert.Equal(3, textLine.TextRuns.Count); Assert.Equal(3, textLine.TextRuns.Count);
Assert.True(textLine.TextRuns[1] is RectangleRun); Assert.True(textLine.TextRuns[1] is RectangleRun);
@ -655,6 +696,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
Assert.NotNull(textLine.TextLineBreak); Assert.NotNull(textLine.TextLineBreak);
Assert.Equal(TextRun.DefaultTextSourceLength, textLine.Length); Assert.Equal(TextRun.DefaultTextSourceLength, textLine.Length);
@ -690,20 +733,22 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var pos = 0; var pos = 0;
TextLineBreak previousLineBreak = null; TextLineBreak? previousLineBreak = null;
TextLine textLine = null; TextLine? textLine = null;
while (pos < text.Length) while (pos < text.Length)
{ {
textLine = TextFormatter.Current.FormatLine(textSource, pos, 30, paragraphProperties, previousLineBreak); textLine = TextFormatter.Current.FormatLine(textSource, pos, 30, paragraphProperties, previousLineBreak);
Assert.NotNull(textLine);
pos += textLine.Length; pos += textLine.Length;
previousLineBreak = textLine.TextLineBreak; previousLineBreak = textLine.TextLineBreak;
} }
Assert.NotNull(textLine); Assert.NotNull(textLine);
Assert.NotNull(textLine.TextLineBreak);
Assert.NotNull(textLine.TextLineBreak.TextEndOfLine); Assert.NotNull(textLine.TextLineBreak.TextEndOfLine);
} }
} }
@ -729,6 +774,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties); TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
void VerifyHit(int offset) void VerifyHit(int offset)
{ {
var glyphCenter = textLine.GetTextBounds(offset, 1)[0].Rectangle.Center; var glyphCenter = textLine.GetTextBounds(offset, 1)[0].Rectangle.Center;
@ -756,6 +803,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = var textLine =
TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties); TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
var bounds = textLine.GetTextBounds(0, 3); var bounds = textLine.GetTextBounds(0, 3);
Assert.Equal(1, bounds.Count); Assert.Equal(1, bounds.Count);
@ -779,6 +828,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{ {
var source = new ListTextSource(new RectangleRun(new Rect(0, 0, 200, 10), Brushes.Aqua)); var source = new ListTextSource(new RectangleRun(new Rect(0, 0, 200, 10), Brushes.Aqua));
var textLine = TextFormatter.Current.FormatLine(source, 0, 100, paragraphProperties); var textLine = TextFormatter.Current.FormatLine(source, 0, 100, paragraphProperties);
Assert.NotNull(textLine);
Assert.Equal(200d, textLine.WidthIncludingTrailingWhitespace); Assert.Equal(200d, textLine.WidthIncludingTrailingWhitespace);
} }
} }
@ -822,6 +872,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
if (textLine.TextLineBreak is {} eol && eol.TextEndOfLine is TextEndOfParagraph) if (textLine.TextLineBreak is {} eol && eol.TextEndOfLine is TextEndOfParagraph)
break; break;
} }
Assert.NotEmpty(lines);
} }
} }
@ -832,13 +884,13 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
DefaultTextRunProperties = defaultTextRunProperties; DefaultTextRunProperties = defaultTextRunProperties;
} }
public override FlowDirection FlowDirection { get; } public override FlowDirection FlowDirection => default;
public override TextAlignment TextAlignment { get; } public override TextAlignment TextAlignment => default;
public override double LineHeight { get; } public override double LineHeight => default;
public override bool FirstLineInParagraph { get; } public override bool FirstLineInParagraph => default;
public override TextRunProperties DefaultTextRunProperties { get; } public override TextRunProperties DefaultTextRunProperties { get; }
public override TextWrapping TextWrapping { get; } public override TextWrapping TextWrapping => default;
public override double Indent { get; } public override double Indent => default;
public override double DefaultIncrementalTab => 64; public override double DefaultIncrementalTab => 64;
} }
@ -858,12 +910,12 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var source = new ListTextSource(text); var source = new ListTextSource(text);
var textLine = TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties); var textLine = TextFormatter.Current.FormatLine(source, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine);
var backspaceHit = textLine.GetBackspaceCaretCharacterHit(new CharacterHit(2)); var backspaceHit = textLine.GetBackspaceCaretCharacterHit(new CharacterHit(2));
Assert.Equal(1, backspaceHit.FirstCharacterIndex); Assert.Equal(1, backspaceHit.FirstCharacterIndex);
Assert.Equal(0, backspaceHit.TrailingLength); Assert.Equal(0, backspaceHit.TrailingLength);
} }
} }
[Fact] [Fact]
@ -878,6 +930,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine = TextFormatter.Current.FormatLine(new SimpleTextSource(text, defaultRunProperties), 0, 120, paragraphProperties); var textLine = TextFormatter.Current.FormatLine(new SimpleTextSource(text, defaultRunProperties), 0, 120, paragraphProperties);
Assert.NotNull(textLine);
Assert.Equal(3, textLine.TextRuns.Count); Assert.Equal(3, textLine.TextRuns.Count);
} }
} }
@ -913,7 +966,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
private class EmptyTextSource : ITextSource private class EmptyTextSource : ITextSource
{ {
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
return null; return null;
} }
@ -936,7 +989,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
_text = text; _text = text;
} }
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length) if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length)
{ {
@ -954,7 +1007,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
private class ListTextSource : ITextSource private class ListTextSource : ITextSource
{ {
private List<TextRun> _runs = new(); private readonly List<TextRun> _runs;
public ListTextSource(params TextRun[] runs) : this((IEnumerable<TextRun>)runs) public ListTextSource(params TextRun[] runs) : this((IEnumerable<TextRun>)runs)
{ {
@ -966,7 +1019,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
_runs = runs.ToList(); _runs = runs.ToList();
} }
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
var off = 0; var off = 0;
for (var c = 0; c < _runs.Count; c++) for (var c = 0; c < _runs.Count; c++)

78
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -1,4 +1,6 @@
using System; #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@ -33,6 +35,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity, formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var firstCharacterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(int.MinValue)); var firstCharacterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(int.MinValue));
Assert.Equal(textLine.FirstTextSourceIndex, firstCharacterHit.FirstCharacterIndex); Assert.Equal(textLine.FirstTextSourceIndex, firstCharacterHit.FirstCharacterIndex);
@ -61,6 +65,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity, formatter.FormatLine(textSource, currentIndex, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var lastCharacterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(int.MaxValue)); var lastCharacterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(int.MaxValue));
Assert.Equal(textLine.FirstTextSourceIndex + textLine.Length, Assert.Equal(textLine.FirstTextSourceIndex + textLine.Length,
@ -88,6 +94,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var clusters = new List<int>(); var clusters = new List<int>();
foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text))) foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text)))
@ -135,6 +143,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var clusters = new List<int>(); var clusters = new List<int>();
foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text))) foreach (var textRun in textLine.TextRuns.OrderBy(x => TextTestHelper.GetStartCharIndex(x.Text)))
@ -187,6 +197,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var clusters = BuildGlyphClusters(textLine); var clusters = BuildGlyphClusters(textLine);
var nextCharacterHit = new CharacterHit(0); var nextCharacterHit = new CharacterHit(0);
@ -246,6 +258,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var clusters = textLine.TextRuns var clusters = textLine.TextRuns
.Cast<ShapedTextRun>() .Cast<ShapedTextRun>()
.SelectMany(x => x.ShapedBuffer, (_, glyph) => glyph.GlyphCluster) .SelectMany(x => x.ShapedBuffer, (_, glyph) => glyph.GlyphCluster)
@ -296,6 +310,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var currentDistance = 0.0; var currentDistance = 0.0;
foreach (var run in textLine.TextRuns) foreach (var run in textLine.TextRuns)
@ -341,6 +357,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var isRightToLeft = IsRightToLeft(textLine); var isRightToLeft = IsRightToLeft(textLine);
var rects = BuildRects(textLine); var rects = BuildRects(textLine);
var glyphClusters = BuildGlyphClusters(textLine); var glyphClusters = BuildGlyphClusters(textLine);
@ -394,6 +412,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.False(textLine.HasCollapsed); Assert.False(textLine.HasCollapsed);
TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties, FlowDirection.LeftToRight)); TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties, FlowDirection.LeftToRight));
@ -427,6 +447,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(4, textLine.TextRuns.Count); Assert.Equal(4, textLine.TextRuns.Count);
var currentHit = textLine.GetNextCaretCharacterHit(new CharacterHit(0)); var currentHit = textLine.GetNextCaretCharacterHit(new CharacterHit(0));
@ -465,6 +487,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
Assert.Equal(4, textLine.TextRuns.Count); Assert.Equal(4, textLine.TextRuns.Count);
var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3, 1)); var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3, 1));
@ -503,6 +527,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var characterHit = textLine.GetCharacterHitFromDistance(50); var characterHit = textLine.GetCharacterHitFromDistance(50);
Assert.Equal(5, characterHit.FirstCharacterIndex); Assert.Equal(5, characterHit.FirstCharacterIndex);
@ -529,6 +555,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(1)); var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(1));
Assert.Equal(14, distance); Assert.Equal(14, distance);
@ -553,6 +581,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(10)); var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(10));
Assert.Equal(72.01171875, distance); Assert.Equal(72.01171875, distance);
@ -585,6 +615,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, 10); var textBounds = textLine.GetTextBounds(0, 10);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -625,6 +657,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, Environment.NewLine.Length); var textBounds = textLine.GetTextBounds(0, Environment.NewLine.Length);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -652,13 +686,15 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var textRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList(); var textRuns = textLine.TextRuns.Cast<ShapedTextRun>().ToList();
var lineWidth = textLine.WidthIncludingTrailingWhitespace; var lineWidth = textLine.WidthIncludingTrailingWhitespace;
var textBounds = textLine.GetTextBounds(0, text.Length); var textBounds = textLine.GetTextBounds(0, text.Length);
TextBounds lastBounds = null; TextBounds? lastBounds = null;
var runBounds = textBounds.SelectMany(x => x.TextRunBounds).ToList(); var runBounds = textBounds.SelectMany(x => x.TextRunBounds).ToList();
@ -711,6 +747,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, 1000, formatter.FormatLine(textSource, 0, 1000,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var characterHit = textLine.GetCharacterHitFromDistance(1000); var characterHit = textLine.GetCharacterHitFromDistance(1000);
Assert.Equal(10, characterHit.FirstCharacterIndex); Assert.Equal(10, characterHit.FirstCharacterIndex);
@ -732,6 +770,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(9, 1)); var characterHit = textLine.GetNextCaretCharacterHit(new CharacterHit(9, 1));
Assert.Equal(10, characterHit.FirstCharacterIndex); Assert.Equal(10, characterHit.FirstCharacterIndex);
@ -784,6 +824,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(20, 1)); var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(20, 1));
Assert.Equal(20, characterHit.FirstCharacterIndex); Assert.Equal(20, characterHit.FirstCharacterIndex);
@ -836,6 +878,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 20, double.PositiveInfinity, formatter.FormatLine(textSource, 20, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var characterHit = textLine.GetCharacterHitFromDistance(double.PositiveInfinity); var characterHit = textLine.GetCharacterHitFromDistance(double.PositiveInfinity);
Assert.Equal(40, characterHit.FirstCharacterIndex + characterHit.TrailingLength); Assert.Equal(40, characterHit.FirstCharacterIndex + characterHit.TrailingLength);
@ -866,7 +910,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{ {
private const string Text = "_A_A"; private const string Text = "_A_A";
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
switch (textSourceIndex) switch (textSourceIndex)
{ {
@ -1004,6 +1048,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
formatter.FormatLine(textSource, 0, double.PositiveInfinity, formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties)); new GenericTextParagraphProperties(defaultProperties));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, textLine.Length); var textBounds = textLine.GetTextBounds(0, textLine.Length);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -1047,16 +1093,18 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, 3); var textBounds = textLine.GetTextBounds(0, 3);
var firstRun = textLine.TextRuns[0] as ShapedTextRun; var firstRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[0]);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); Assert.Equal(firstRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
textBounds = textLine.GetTextBounds(3, 4); textBounds = textLine.GetTextBounds(3, 4);
var secondRun = textLine.TextRuns[1] as ShapedTextRun; var secondRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[1]);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
@ -1094,16 +1142,18 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, 4); var textBounds = textLine.GetTextBounds(0, 4);
var secondRun = textLine.TextRuns[1] as ShapedTextRun; var secondRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[1]);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width)); Assert.Equal(secondRun.Size.Width, textBounds.Sum(x => x.Rectangle.Width));
textBounds = textLine.GetTextBounds(4, 3); textBounds = textLine.GetTextBounds(4, 3);
var firstRun = textLine.TextRuns[0] as ShapedTextRun; var firstRun = Assert.IsType<ShapedTextRun>(textLine.TextRuns[0]);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -1146,6 +1196,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(0, 1); var textBounds = textLine.GetTextBounds(0, 1);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -1173,6 +1225,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var textBounds = textLine.GetTextBounds(3, 1); var textBounds = textLine.GetTextBounds(3, 1);
Assert.Equal(1, textBounds.Count); Assert.Equal(1, textBounds.Count);
@ -1200,6 +1254,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var bounds = textLine.GetTextBounds(6, 1); var bounds = textLine.GetTextBounds(6, 1);
Assert.Equal(1, bounds.Count); Assert.Equal(1, bounds.Count);
@ -1249,6 +1305,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var bounds = textLine.GetTextBounds(0, text.Length); var bounds = textLine.GetTextBounds(0, text.Length);
Assert.Equal(4, bounds.Count); Assert.Equal(4, bounds.Count);
@ -1259,8 +1317,6 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
} }
} }
[Fact] [Fact]
public void Should_GetPreviousCharacterHit_Non_Trailing() public void Should_GetPreviousCharacterHit_Non_Trailing()
{ {
@ -1278,6 +1334,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left, new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0)); true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(10, 1)); var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(10, 1));
} }
} }
@ -1291,7 +1349,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
_textRuns = textRuns; _textRuns = textRuns;
} }
public TextRun GetTextRun(int textSourceIndex) public TextRun? GetTextRun(int textSourceIndex)
{ {
var currentPosition = 0; var currentPosition = 0;

Loading…
Cancel
Save