Browse Source

Merge branch 'master' into fast-theme-switching-fluent-theme

pull/7256/head
Max Katz 5 years ago
committed by GitHub
parent
commit
3b22aefb99
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/Avalonia/Avalonia.csproj
  2. 6
      readme.md
  3. 8
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  4. 3
      src/Android/Avalonia.Android/AndroidPlatform.cs
  5. 56
      src/Android/Avalonia.Android/Stubs.cs
  6. 2
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  7. 2
      src/Avalonia.Animation/Avalonia.Animation.csproj
  8. 2
      src/Avalonia.Base/Avalonia.Base.csproj
  9. 10
      src/Avalonia.Base/AvaloniaLocator.cs
  10. 6
      src/Avalonia.Base/Threading/Dispatcher.cs
  11. 2
      src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs
  12. 2
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  13. 2
      src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
  14. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  15. 17
      src/Avalonia.Controls/Button.cs
  16. 68
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  17. 2
      src/Avalonia.Controls/Grid.cs
  18. 9
      src/Avalonia.Controls/SystemDialog.cs
  19. 2
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  20. 2
      src/Avalonia.Desktop/Avalonia.Desktop.csproj
  21. 2
      src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
  22. 2
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  23. 7
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  24. 2
      src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
  25. 2
      src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
  26. 2
      src/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj
  27. 2
      src/Avalonia.Headless/Avalonia.Headless.csproj
  28. 2
      src/Avalonia.Input/AccessKeyHandler.cs
  29. 2
      src/Avalonia.Input/Avalonia.Input.csproj
  30. 4
      src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs
  31. 18
      src/Avalonia.Input/Gestures.cs
  32. 2
      src/Avalonia.Input/InputElement.cs
  33. 2
      src/Avalonia.Input/Pointer.cs
  34. 9
      src/Avalonia.Input/PointerEventArgs.cs
  35. 44
      src/Avalonia.Input/TouchDevice.cs
  36. 2
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  37. 6
      src/Avalonia.Layout/AttachedLayout.cs
  38. 3
      src/Avalonia.Layout/Avalonia.Layout.csproj
  39. 46
      src/Avalonia.Layout/ElementManager.cs
  40. 68
      src/Avalonia.Layout/FlowLayoutAlgorithm.cs
  41. 4
      src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs
  42. 4
      src/Avalonia.Layout/LayoutContext.cs
  43. 2
      src/Avalonia.Layout/LayoutContextAdapter.cs
  44. 2
      src/Avalonia.Layout/LayoutHelper.cs
  45. 1
      src/Avalonia.Layout/LayoutQueue.cs
  46. 2
      src/Avalonia.Layout/Layoutable.cs
  47. 4
      src/Avalonia.Layout/NonVirtualizingLayoutContext.cs
  48. 22
      src/Avalonia.Layout/StackLayout.cs
  49. 26
      src/Avalonia.Layout/UniformGridLayout.cs
  50. 2
      src/Avalonia.Layout/UniformGridLayoutState.cs
  51. 5
      src/Avalonia.Layout/Utils/ListUtils.cs
  52. 4
      src/Avalonia.Layout/VirtualLayoutContextAdapter.cs
  53. 2
      src/Avalonia.Layout/VirtualizingLayoutContext.cs
  54. 2
      src/Avalonia.Layout/WrapLayout/UvMeasure.cs
  55. 2
      src/Avalonia.Layout/WrapLayout/WrapItem.cs
  56. 6
      src/Avalonia.Layout/WrapLayout/WrapLayout.cs
  57. 9
      src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs
  58. 2
      src/Avalonia.Native/Avalonia.Native.csproj
  59. 7
      src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
  60. 5
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  61. 2
      src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
  62. 2
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  63. 2
      src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
  64. 3
      src/Avalonia.Styling/Avalonia.Styling.csproj
  65. 12
      src/Avalonia.Styling/Controls/ChildNameScope.cs
  66. 4
      src/Avalonia.Styling/Controls/INameScope.cs
  67. 30
      src/Avalonia.Styling/Controls/NameScope.cs
  68. 28
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  69. 8
      src/Avalonia.Styling/Controls/NameScopeLocator.cs
  70. 2
      src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs
  71. 2
      src/Avalonia.Styling/Controls/ResourceDictionary.cs
  72. 6
      src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs
  73. 2
      src/Avalonia.Styling/INamed.cs
  74. 16
      src/Avalonia.Styling/LogicalTree/ControlLocator.cs
  75. 6
      src/Avalonia.Styling/LogicalTree/ILogical.cs
  76. 26
      src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
  77. 7
      src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs
  78. 18
      src/Avalonia.Styling/StyledElement.cs
  79. 2
      src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs
  80. 2
      src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs
  81. 6
      src/Avalonia.Styling/Styling/ChildSelector.cs
  82. 2
      src/Avalonia.Styling/Styling/OrSelector.cs
  83. 7
      src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
  84. 50
      src/Avalonia.Styling/Styling/Selectors.cs
  85. 14
      src/Avalonia.Styling/Styling/Styles.cs
  86. 6
      src/Avalonia.Styling/Styling/TemplateSelector.cs
  87. 2
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  88. 6
      src/Avalonia.Themes.Default/CalendarDatePicker.xaml
  89. 2
      src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
  90. 6
      src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
  91. 2
      src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs
  92. 4
      src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs
  93. 2
      src/Avalonia.Visuals/Animation/CompositePageTransition.cs
  94. 6
      src/Avalonia.Visuals/Animation/CrossFade.cs
  95. 2
      src/Avalonia.Visuals/Animation/IPageTransition.cs
  96. 10
      src/Avalonia.Visuals/Animation/PageSlide.cs
  97. 2
      src/Avalonia.Visuals/Animation/RenderLoopClock.cs
  98. 4
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  99. 3
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  100. 2
      src/Avalonia.Visuals/CornerRadius.cs

2
packages/Avalonia/Avalonia.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;netcoreapp2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0;net461;netcoreapp2.0</TargetFrameworks>
<PackageId>Avalonia</PackageId> <PackageId>Avalonia</PackageId>
</PropertyGroup> </PropertyGroup>

6
readme.md

@ -75,6 +75,12 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou
Avalonia is licenced under the [MIT licence](licence.md). Avalonia is licenced under the [MIT licence](licence.md).
## Support Avalonia
**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx
This will be shared with the community and awarded for significant contributions.
### Backers ### Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Avalonia#backer)] Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Avalonia#backer)]

8
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -32,7 +32,7 @@
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies> <BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi> <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi-v7a;x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger> <Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex> <AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<AotAssemblies>False</AotAssemblies> <AotAssemblies>False</AotAssemblies>
@ -51,7 +51,7 @@
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies> <BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi> <AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi-v7a,x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger> <Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies> <AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM> <EnableLLVM>False</EnableLLVM>
@ -125,6 +125,10 @@
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name> <Name>Avalonia.Layout</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj">
<Project>{c42d2fc1-a531-4ed4-84b9-89aec7c962fc}</Project>
<Name>Avalonia.Themes.Fluent</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> <ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name> <Name>Avalonia.Visuals</Name>

3
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -43,11 +43,12 @@ namespace Avalonia.Android
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToTransient<ClipboardImpl>() .Bind<IClipboard>().ToTransient<ClipboardImpl>()
.Bind<ICursorFactory>().ToTransient<CursorFactory>() .Bind<ICursorFactory>().ToTransient<CursorFactory>()
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>() .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance) .Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface()) .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>()
.Bind<IRenderTimer>().ToConstant(new ChoreographerTimer()) .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>() .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()

56
src/Android/Avalonia.Android/Stubs.cs

@ -0,0 +1,56 @@
using System;
using System.IO;
using Avalonia.Platform;
namespace Avalonia.Android
{
class WindowingPlatformStub : IWindowingPlatform
{
public IWindowImpl CreateWindow() => throw new NotSupportedException();
public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
public ITrayIconImpl CreateTrayIcon() => null;
}
class PlatformIconLoaderStub : IPlatformIconLoader
{
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{
using (var stream = new MemoryStream())
{
bitmap.Save(stream);
return LoadIcon(stream);
}
}
public IWindowIconImpl LoadIcon(Stream stream)
{
var ms = new MemoryStream();
stream.CopyTo(ms);
return new IconStub(ms);
}
public IWindowIconImpl LoadIcon(string fileName)
{
using (var file = File.Open(fileName, FileMode.Open))
return LoadIcon(file);
}
}
public class IconStub : IWindowIconImpl
{
private readonly MemoryStream _ms;
public IconStub(MemoryStream stream)
{
_ms = stream;
}
public void Save(Stream outputStream)
{
_ms.Position = 0;
_ms.CopyTo(outputStream);
}
}
}

2
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication
{ {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource public partial class Resource
{ {

2
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />

2
src/Avalonia.Base/Avalonia.Base.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AssemblyName>Avalonia.Base</AssemblyName> <AssemblyName>Avalonia.Base</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>

10
src/Avalonia.Base/AvaloniaLocator.cs

@ -125,6 +125,16 @@ namespace Avalonia
{ {
return (T?) resolver.GetService(typeof (T)); return (T?) resolver.GetService(typeof (T));
} }
public static object GetRequiredService(this IAvaloniaDependencyResolver resolver, Type t)
{
return resolver.GetService(t) ?? throw new InvalidOperationException($"Unable to locate '{t}'.");
}
public static T GetRequiredService<T>(this IAvaloniaDependencyResolver resolver)
{
return (T?)resolver.GetService(typeof(T)) ?? throw new InvalidOperationException($"Unable to locate '{typeof(T)}'.");
}
} }
} }

6
src/Avalonia.Base/Threading/Dispatcher.cs

@ -56,11 +56,7 @@ namespace Avalonia.Threading
/// </param> /// </param>
public void MainLoop(CancellationToken cancellationToken) public void MainLoop(CancellationToken cancellationToken)
{ {
var platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>(); var platform = AvaloniaLocator.Current.GetRequiredService<IPlatformThreadingInterface>();
if (platform is null)
throw new InvalidOperationException("Unable to locate IPlatformThreadingInterface");
cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send)); cancellationToken.Register(() => platform.Signal(DispatcherPriority.Send));
platform.RunLoop(cancellationToken); platform.RunLoop(cancellationToken);
} }

2
src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs

@ -180,7 +180,7 @@ namespace Avalonia.Utilities
{ {
var r = _data[c]; var r = _data[c];
if (r?.TryGetTarget(out var sub) == true) if (r?.TryGetTarget(out var sub) == true)
sub.OnEvent(sender, eventArgs); sub!.OnEvent(sender, eventArgs);
else else
needCompact = true; needCompact = true;
} }

2
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -6,7 +6,7 @@
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants> <DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>NU1605</NoWarn> <NoWarn>NU1605;CS8632</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<PackageId>Avalonia.Controls.DataGrid</PackageId> <PackageId>Avalonia.Controls.DataGrid</PackageId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> <Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />

17
src/Avalonia.Controls/Button.cs

@ -304,15 +304,18 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
protected virtual void OnClick() protected virtual void OnClick()
{ {
OpenFlyout(); if (IsEffectivelyEnabled)
{
OpenFlyout();
var e = new RoutedEventArgs(ClickEvent); var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e); RaiseEvent(e);
if (!e.Handled && Command?.CanExecute(CommandParameter) == true) if (!e.Handled && Command?.CanExecute(CommandParameter) == true)
{ {
Command.Execute(CommandParameter); Command.Execute(CommandParameter);
e.Handled = true; e.Handled = true;
}
} }
} }

68
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -1,4 +1,4 @@
// (c) Copyright Microsoft Corporation. // (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL). // This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
@ -425,9 +425,6 @@ namespace Avalonia.Controls
{ {
FocusableProperty.OverrideDefaultValue<CalendarDatePicker>(true); FocusableProperty.OverrideDefaultValue<CalendarDatePicker>(true);
DisplayDateProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateEndChanged(e));
IsDropDownOpenProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnIsDropDownOpenChanged(e)); IsDropDownOpenProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnIsDropDownOpenChanged(e));
SelectedDateProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateChanged(e)); SelectedDateProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateChanged(e));
SelectedDateFormatProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateFormatChanged(e)); SelectedDateFormatProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateFormatChanged(e));
@ -459,18 +456,12 @@ namespace Avalonia.Controls
if (_calendar != null) if (_calendar != null)
{ {
_calendar.SelectionMode = CalendarSelectionMode.SingleDate; _calendar.SelectionMode = CalendarSelectionMode.SingleDate;
_calendar.SelectedDate = SelectedDate;
SetCalendarDisplayDate(DisplayDate);
SetCalendarDisplayDateStart(DisplayDateStart);
SetCalendarDisplayDateEnd(DisplayDateEnd);
_calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp; _calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp;
_calendar.DisplayDateChanged += Calendar_DisplayDateChanged; _calendar.DisplayDateChanged += Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged; _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
_calendar.PointerReleased += Calendar_PointerReleased; _calendar.PointerReleased += Calendar_PointerReleased;
_calendar.KeyDown += Calendar_KeyDown; _calendar.KeyDown += Calendar_KeyDown;
//_calendar.SizeChanged += new SizeChangedEventHandler(Calendar_SizeChanged);
//_calendar.IsTabStop = true;
var currentBlackoutDays = BlackoutDates; var currentBlackoutDays = BlackoutDates;
BlackoutDates = _calendar.BlackoutDates; BlackoutDates = _calendar.BlackoutDates;
@ -587,58 +578,6 @@ namespace Avalonia.Controls
SetSelectedDate(); SetSelectedDate();
} }
private void SetCalendarDisplayDate(DateTime value)
{
if (DateTimeHelper.CompareYearMonth(_calendar.DisplayDate, value) != 0)
{
_calendar.DisplayDate = DisplayDate;
if (DateTime.Compare(_calendar.DisplayDate, DisplayDate) != 0)
{
DisplayDate = _calendar.DisplayDate;
}
}
}
private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e)
{
if (_calendar != null)
{
var value = (DateTime)e.NewValue;
SetCalendarDisplayDate(value);
}
}
private void SetCalendarDisplayDateStart(DateTime? value)
{
_calendar.DisplayDateStart = value;
if (_calendar.DisplayDateStart.HasValue && DisplayDateStart.HasValue && DateTime.Compare(_calendar.DisplayDateStart.Value, DisplayDateStart.Value) != 0)
{
DisplayDateStart = _calendar.DisplayDateStart;
}
}
private void OnDisplayDateStartChanged(AvaloniaPropertyChangedEventArgs e)
{
if (_calendar != null)
{
var value = (DateTime?)e.NewValue;
SetCalendarDisplayDateStart(value);
}
}
private void SetCalendarDisplayDateEnd(DateTime? value)
{
_calendar.DisplayDateEnd = value;
if (_calendar.DisplayDateEnd.HasValue && DisplayDateEnd.HasValue && DateTime.Compare(_calendar.DisplayDateEnd.Value, DisplayDateEnd.Value) != 0)
{
DisplayDateEnd = _calendar.DisplayDateEnd;
}
}
private void OnDisplayDateEndChanged(AvaloniaPropertyChangedEventArgs e)
{
if (_calendar != null)
{
var value = (DateTime?)e.NewValue;
SetCalendarDisplayDateEnd(value);
}
}
private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e) private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var oldValue = (bool)e.OldValue; var oldValue = (bool)e.OldValue;
@ -670,11 +609,6 @@ namespace Avalonia.Controls
var addedDate = (DateTime?)e.NewValue; var addedDate = (DateTime?)e.NewValue;
var removedDate = (DateTime?)e.OldValue; var removedDate = (DateTime?)e.OldValue;
if (_calendar != null && addedDate != _calendar.SelectedDate)
{
_calendar.SelectedDate = addedDate;
}
if (SelectedDate != null) if (SelectedDate != null)
{ {
DateTime day = SelectedDate.Value; DateTime day = SelectedDate.Value;

2
src/Avalonia.Controls/Grid.cs

@ -978,7 +978,7 @@ namespace Avalonia.Controls
/// width is not registered in columns.</param> /// width is not registered in columns.</param>
/// <param name="forceInfinityV">Passed through to MeasureCell. /// <param name="forceInfinityV">Passed through to MeasureCell.
/// When "true" cells' desired height is not registered in rows.</param> /// When "true" cells' desired height is not registered in rows.</param>
/// <param name="hasDesiredSizeUChanged">return true when desired size has changed</param> /// <param name="hasDesiredSizeUChanged">When the method exits, indicates whether the desired size has changed.</param>
private void MeasureCellsGroup( private void MeasureCellsGroup(
int cellsHead, int cellsHead,
Size referenceSize, Size referenceSize,

9
src/Avalonia.Controls/SystemDialog.cs

@ -66,8 +66,7 @@ namespace Avalonia.Controls
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
var service = AvaloniaLocator.Current.GetService<ISystemDialogImpl>() ?? var service = AvaloniaLocator.Current.GetRequiredService<ISystemDialogImpl>();
throw new InvalidOperationException("Unable to locate ISystemDialogImpl.");
return (await service.ShowFileDialogAsync(this, parent) ?? return (await service.ShowFileDialogAsync(this, parent) ??
Array.Empty<string>()).FirstOrDefault(); Array.Empty<string>()).FirstOrDefault();
} }
@ -95,8 +94,7 @@ namespace Avalonia.Controls
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
var service = AvaloniaLocator.Current.GetService<ISystemDialogImpl>() ?? var service = AvaloniaLocator.Current.GetRequiredService<ISystemDialogImpl>();
throw new InvalidOperationException("Unable to locate ISystemDialogImpl.");
return service.ShowFileDialogAsync(this, parent); return service.ShowFileDialogAsync(this, parent);
} }
} }
@ -125,8 +123,7 @@ namespace Avalonia.Controls
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
var service = AvaloniaLocator.Current.GetService<ISystemDialogImpl>() ?? var service = AvaloniaLocator.Current.GetRequiredService<ISystemDialogImpl>();
throw new InvalidOperationException("Unable to locate ISystemDialogImpl.");
return service.ShowFolderDialogAsync(this, parent); return service.ShowFolderDialogAsync(this, parent);
} }
} }

2
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<!-- WARNING! The designer support version number needs to be frozen <!-- WARNING! The designer support version number needs to be frozen
To allow projects that implement designer functionality to still To allow projects that implement designer functionality to still
work with newer versions of Avalonia. This version number only work with newer versions of Avalonia. This version number only

2
src/Avalonia.Desktop/Avalonia.Desktop.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<PackageId>Avalonia.Desktop</PackageId> <PackageId>Avalonia.Desktop</PackageId>
</PropertyGroup> </PropertyGroup>

2
src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;net461;netcoreapp2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<PackageId>Avalonia.Diagnostics</PackageId> <PackageId>Avalonia.Diagnostics</PackageId>
</PropertyGroup> </PropertyGroup>

7
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -86,14 +86,15 @@ namespace Avalonia.Diagnostics.ViewModels
public void SelectControl(IControl control) public void SelectControl(IControl control)
{ {
var node = default(TreeNode); var node = default(TreeNode);
IControl? c = control;
while (node == null && control != null) while (node == null && c != null)
{ {
node = FindNode(control); node = FindNode(c);
if (node == null) if (node == null)
{ {
control = control.GetVisualParent<IControl>(); c = c.GetVisualParent<IControl>();
} }
} }

2
src/Avalonia.Dialogs/Avalonia.Dialogs.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Headless/Avalonia.Headless.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Avalonia.Input/AccessKeyHandler.cs

@ -188,7 +188,7 @@ namespace Avalonia.Input
// If the menu is open, only match controls in the menu's visual tree. // If the menu is open, only match controls in the menu's visual tree.
if (menuIsOpen) if (menuIsOpen)
{ {
matches = matches.Where(x => MainMenu.IsVisualAncestorOf(x)); matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x));
} }
var match = matches.FirstOrDefault(); var match = matches.FirstOrDefault();

2
src/Avalonia.Input/Avalonia.Input.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> <Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />

4
src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -71,7 +71,7 @@ namespace Avalonia.Input.GestureRecognizers
EndGesture(); EndGesture();
_tracking = e.Pointer; _tracking = e.Pointer;
_gestureId = ScrollGestureEventArgs.GetNextFreeId();; _gestureId = ScrollGestureEventArgs.GetNextFreeId();;
_trackedRootPoint = e.GetPosition(null); _trackedRootPoint = e.GetPosition(_target);
} }
} }
@ -85,7 +85,7 @@ namespace Avalonia.Input.GestureRecognizers
{ {
if (e.Pointer == _tracking) if (e.Pointer == _tracking)
{ {
var rootPoint = e.GetPosition(null); var rootPoint = e.GetPosition(_target);
if (!_scrolling) if (!_scrolling)
{ {
if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance) if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)

18
src/Avalonia.Input/Gestures.cs

@ -6,6 +6,7 @@ namespace Avalonia.Input
{ {
public static class Gestures public static class Gestures
{ {
private static bool s_isDoubleTapped = false;
public static readonly RoutedEvent<TappedEventArgs> TappedEvent = RoutedEvent.Register<TappedEventArgs>( public static readonly RoutedEvent<TappedEventArgs> TappedEvent = RoutedEvent.Register<TappedEventArgs>(
"Tapped", "Tapped",
RoutingStrategies.Bubble, RoutingStrategies.Bubble,
@ -81,20 +82,23 @@ namespace Avalonia.Input
var e = (PointerPressedEventArgs)ev; var e = (PointerPressedEventArgs)ev;
var visual = (IVisual)ev.Source; var visual = (IVisual)ev.Source;
#pragma warning disable CS0618 // Type or member is obsolete if (e.ClickCount <= 1)
var clickCount = e.ClickCount;
#pragma warning restore CS0618 // Type or member is obsolete
if (clickCount <= 1)
{ {
s_isDoubleTapped = false;
s_lastPress.SetTarget(ev.Source); s_lastPress.SetTarget(ev.Source);
} }
else if (clickCount == 2 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{ {
if (s_lastPress.TryGetTarget(out var target) && target == e.Source) if (s_lastPress.TryGetTarget(out var target) && target == e.Source)
{ {
s_isDoubleTapped = true;
e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
} }
} }
else
{
s_isDoubleTapped = false;
}
} }
} }
@ -112,7 +116,9 @@ namespace Avalonia.Input
{ {
e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
} }
else //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
//This behaviour matches UWP behaviour.
else if (s_isDoubleTapped == false)
{ {
e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e)); e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e));
} }

2
src/Avalonia.Input/InputElement.cs

@ -675,7 +675,7 @@ namespace Avalonia.Input
/// <see cref="IsEffectivelyEnabled"/>. /// <see cref="IsEffectivelyEnabled"/>.
/// </summary> /// </summary>
/// <param name="parent">The parent control.</param> /// <param name="parent">The parent control.</param>
private void UpdateIsEffectivelyEnabled(InputElement parent) private void UpdateIsEffectivelyEnabled(InputElement? parent)
{ {
IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true); IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true);

2
src/Avalonia.Input/Pointer.cs

@ -54,7 +54,7 @@ namespace Avalonia.Input
Captured.DetachedFromVisualTree += OnCaptureDetached; Captured.DetachedFromVisualTree += OnCaptureDetached;
} }
IInputElement GetNextCapture(IVisual parent) IInputElement? GetNextCapture(IVisual parent)
{ {
return parent as IInputElement ?? parent.FindAncestorOfType<IInputElement>(); return parent as IInputElement ?? parent.FindAncestorOfType<IInputElement>();
} }

9
src/Avalonia.Input/PointerEventArgs.cs

@ -114,7 +114,7 @@ namespace Avalonia.Input
public class PointerPressedEventArgs : PointerEventArgs public class PointerPressedEventArgs : PointerEventArgs
{ {
private readonly int _obsoleteClickCount; private readonly int _clickCount;
public PointerPressedEventArgs( public PointerPressedEventArgs(
IInteractive source, IInteractive source,
@ -123,15 +123,14 @@ namespace Avalonia.Input
ulong timestamp, ulong timestamp,
PointerPointProperties properties, PointerPointProperties properties,
KeyModifiers modifiers, KeyModifiers modifiers,
int obsoleteClickCount = 1) int clickCount = 1)
: base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition,
timestamp, properties, modifiers) timestamp, properties, modifiers)
{ {
_obsoleteClickCount = obsoleteClickCount; _clickCount = clickCount;
} }
[Obsolete("Use DoubleTapped event or Gestures.DoubleRightTapped attached event")] public int ClickCount => _clickCount;
public int ClickCount => _obsoleteClickCount;
[Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")] [Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")]
public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton(); public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton();

44
src/Avalonia.Input/TouchDevice.cs

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.VisualTree; using Avalonia.Platform;
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -16,7 +16,9 @@ namespace Avalonia.Input
{ {
private readonly Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>(); private readonly Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>();
private bool _disposed; private bool _disposed;
private int _clickCount;
private Rect _lastClickRect;
private ulong _lastClickTime;
KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) => KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) =>
(KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask);
@ -27,10 +29,10 @@ namespace Avalonia.Input
rv |= RawInputModifiers.LeftMouseButton; rv |= RawInputModifiers.LeftMouseButton;
return rv; return rv;
} }
public void ProcessRawEvent(RawInputEventArgs ev) public void ProcessRawEvent(RawInputEventArgs ev)
{ {
if(_disposed) if (_disposed)
return; return;
var args = (RawTouchEventArgs)ev; var args = (RawTouchEventArgs)ev;
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer)) if (!_pointers.TryGetValue(args.TouchPointId, out var pointer))
@ -43,16 +45,40 @@ namespace Avalonia.Input
PointerType.Touch, _pointers.Count == 0); PointerType.Touch, _pointers.Count == 0);
pointer.Capture(hit); pointer.Capture(hit);
} }
var target = pointer.Captured ?? args.Root; var target = pointer.Captured ?? args.Root;
if (args.Type == RawPointerEventType.TouchBegin) if (args.Type == RawPointerEventType.TouchBegin)
{ {
if (_pointers.Count > 1)
{
_clickCount = 1;
_lastClickTime = 0;
_lastClickRect = new Rect();
}
else
{
var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
if (settings == null)
{
throw new Exception("IPlatformSettings can not be null.");
}
if (!_lastClickRect.Contains(args.Position)
|| ev.Timestamp - _lastClickTime > settings.DoubleClickTime.TotalMilliseconds)
{
_clickCount = 0;
}
++_clickCount;
_lastClickTime = ev.Timestamp;
_lastClickRect = new Rect(args.Position, new Size())
.Inflate(new Thickness(16, 16));
}
target.RaiseEvent(new PointerPressedEventArgs(target, pointer, target.RaiseEvent(new PointerPressedEventArgs(target, pointer,
args.Root, args.Position, ev.Timestamp, args.Root, args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, true), new PointerPointProperties(GetModifiers(args.InputModifiers, true),
PointerUpdateKind.LeftButtonPressed), PointerUpdateKind.LeftButtonPressed),
GetKeyModifiers(args.InputModifiers))); GetKeyModifiers(args.InputModifiers), _clickCount));
} }
if (args.Type == RawPointerEventType.TouchEnd) if (args.Type == RawPointerEventType.TouchEnd)
@ -84,12 +110,12 @@ namespace Avalonia.Input
GetKeyModifiers(args.InputModifiers))); GetKeyModifiers(args.InputModifiers)));
} }
} }
public void Dispose() public void Dispose()
{ {
if(_disposed) if (_disposed)
return; return;
var values = _pointers.Values.ToList(); var values = _pointers.Values.ToList();
_pointers.Clear(); _pointers.Clear();
@ -97,6 +123,6 @@ namespace Avalonia.Input
foreach (var p in values) foreach (var p in values)
p.Dispose(); p.Dispose();
} }
} }
} }

2
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

6
src/Avalonia.Layout/AttachedLayout.cs

@ -12,17 +12,17 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class AttachedLayout : AvaloniaObject public abstract class AttachedLayout : AvaloniaObject
{ {
public string LayoutId { get; set; } public string? LayoutId { get; set; }
/// <summary> /// <summary>
/// Occurs when the measurement state (layout) has been invalidated. /// Occurs when the measurement state (layout) has been invalidated.
/// </summary> /// </summary>
public event EventHandler MeasureInvalidated; public event EventHandler? MeasureInvalidated;
/// <summary> /// <summary>
/// Occurs when the arrange state (layout) has been invalidated. /// Occurs when the arrange state (layout) has been invalidated.
/// </summary> /// </summary>
public event EventHandler ArrangeInvalidated; public event EventHandler? ArrangeInvalidated;
/// <summary> /// <summary>
/// Initializes any per-container state the layout requires when it is attached to an /// Initializes any per-container state the layout requires when it is attached to an

3
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -9,4 +9,5 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

46
src/Avalonia.Layout/ElementManager.cs

@ -13,10 +13,10 @@ namespace Avalonia.Layout
{ {
internal class ElementManager internal class ElementManager
{ {
private readonly List<ILayoutable> _realizedElements = new List<ILayoutable>(); private readonly List<ILayoutable?> _realizedElements = new List<ILayoutable?>();
private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>(); private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>();
private int _firstRealizedDataIndex; private int _firstRealizedDataIndex;
private VirtualizingLayoutContext _context; private VirtualizingLayoutContext? _context;
private bool IsVirtualizingContext private bool IsVirtualizingContext
{ {
@ -58,7 +58,7 @@ namespace Avalonia.Layout
// Make sure there is enough space for the bounds. // Make sure there is enough space for the bounds.
// Note: We could optimize when the count becomes smaller, but keeping // Note: We could optimize when the count becomes smaller, but keeping
// it always up to date is the simplest option for now. // it always up to date is the simplest option for now.
_realizedElementLayoutBounds.Resize(count); _realizedElementLayoutBounds.Resize(count, default);
} }
} }
} }
@ -66,34 +66,32 @@ namespace Avalonia.Layout
public int GetRealizedElementCount() public int GetRealizedElementCount()
{ {
return IsVirtualizingContext ? _realizedElements.Count : _context.ItemCount; return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount;
} }
public ILayoutable GetAt(int realizedIndex) public ILayoutable GetAt(int realizedIndex)
{ {
ILayoutable element; ILayoutable? element;
if (IsVirtualizingContext) if (IsVirtualizingContext)
{ {
if (_realizedElements[realizedIndex] == null) element = _realizedElements[realizedIndex];
if (element == null)
{ {
// Sentinel. Create the element now since we need it. // Sentinel. Create the element now since we need it.
int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex); int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "Creating element for sentinal with data index {Index}", dataIndex);
element = _context.GetOrCreateElementAt( element = _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
_realizedElements[realizedIndex] = element; _realizedElements[realizedIndex] = element;
} }
else
{
element = _realizedElements[realizedIndex];
}
} }
else else
{ {
// realizedIndex and dataIndex are the same (everything is realized) // realizedIndex and dataIndex are the same (everything is realized)
element = _context.GetOrCreateElementAt( element = _context!.GetOrCreateElementAt(
realizedIndex, realizedIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
} }
@ -112,7 +110,7 @@ namespace Avalonia.Layout
_realizedElementLayoutBounds.Add(default); _realizedElementLayoutBounds.Add(default);
} }
public void Insert(int realizedIndex, int dataIndex, ILayoutable element) public void Insert(int realizedIndex, int dataIndex, ILayoutable? element)
{ {
if (realizedIndex == 0) if (realizedIndex == 0)
{ {
@ -136,7 +134,7 @@ namespace Avalonia.Layout
if (elementRef != null) if (elementRef != null)
{ {
_context.RecycleElement(elementRef); _context!.RecycleElement(elementRef);
} }
} }
@ -203,26 +201,26 @@ namespace Avalonia.Layout
else else
{ {
// Non virtualized - everything is realized // Non virtualized - everything is realized
return index >= 0 && index < _context.ItemCount; return index >= 0 && index < _context!.ItemCount;
} }
} }
public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context.ItemCount; public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context!.ItemCount;
public ILayoutable GetRealizedElement(int dataIndex) public ILayoutable? GetRealizedElement(int dataIndex)
{ {
return IsVirtualizingContext ? return IsVirtualizingContext ?
GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) :
_context.GetOrCreateElementAt( _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
} }
public void EnsureElementRealized(bool forward, int dataIndex, string layoutId) public void EnsureElementRealized(bool forward, int dataIndex, string? layoutId)
{ {
if (IsDataIndexRealized(dataIndex) == false) if (IsDataIndexRealized(dataIndex) == false)
{ {
var element = _context.GetOrCreateElementAt( var element = _context!.GetOrCreateElementAt(
dataIndex, dataIndex,
ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
@ -273,14 +271,14 @@ namespace Avalonia.Layout
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
{ {
OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); OnItemsAdded(args.NewStartingIndex, args.NewItems!.Count);
} }
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
{ {
int oldSize = args.OldItems.Count; int oldSize = args.OldItems!.Count;
int newSize = args.NewItems.Count; int newSize = args.NewItems!.Count;
int oldStartIndex = args.OldStartingIndex; int oldStartIndex = args.OldStartingIndex;
int newStartIndex = args.NewStartingIndex; int newStartIndex = args.NewStartingIndex;
@ -301,7 +299,7 @@ namespace Avalonia.Layout
if (elementRef != null) if (elementRef != null)
{ {
_context.RecycleElement(elementRef); _context!.RecycleElement(elementRef);
_realizedElements[realizedIndex] = null; _realizedElements[realizedIndex] = null;
} }
} }

68
src/Avalonia.Layout/FlowLayoutAlgorithm.cs

@ -16,8 +16,8 @@ namespace Avalonia.Layout
private Size _lastAvailableSize; private Size _lastAvailableSize;
private double _lastItemSpacing; private double _lastItemSpacing;
private bool _collectionChangePending; private bool _collectionChangePending;
private VirtualizingLayoutContext _context; private VirtualizingLayoutContext? _context;
private IFlowLayoutAlgorithmDelegates _algorithmCallbacks; private IFlowLayoutAlgorithmDelegates? _algorithmCallbacks;
private Rect _lastExtent; private Rect _lastExtent;
private int _firstRealizedDataIndexInsideRealizationWindow = -1; private int _firstRealizedDataIndexInsideRealizationWindow = -1;
private int _lastRealizedDataIndexInsideRealizationWindow = -1; private int _lastRealizedDataIndexInsideRealizationWindow = -1;
@ -46,7 +46,7 @@ namespace Avalonia.Layout
} }
} }
private Rect RealizationRect => IsVirtualizingContext ? _context.RealizationRect : new Rect(Size.Infinity); private Rect RealizationRect => IsVirtualizingContext ? _context!.RealizationRect : new Rect(Size.Infinity);
public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks) public void InitializeForContext(VirtualizingLayoutContext context, IFlowLayoutAlgorithmDelegates callbacks)
{ {
@ -76,7 +76,7 @@ namespace Avalonia.Layout
int maxItemsPerLine, int maxItemsPerLine,
ScrollOrientation orientation, ScrollOrientation orientation,
bool disableVirtualization, bool disableVirtualization,
string layoutId) string? layoutId)
{ {
_orientation.ScrollOrientation = orientation; _orientation.ScrollOrientation = orientation;
@ -87,7 +87,7 @@ namespace Avalonia.Layout
layoutId, layoutId,
realizationRect); realizationRect);
var suggestedAnchorIndex = _context.RecommendedAnchorIndex; var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
if (_elementManager.IsIndexValidInData(suggestedAnchorIndex)) if (_elementManager.IsIndexValidInData(suggestedAnchorIndex))
{ {
var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex); var anchorRealized = _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
@ -124,7 +124,7 @@ namespace Avalonia.Layout
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
bool isWrapping, bool isWrapping,
LineAlignment lineAlignment, LineAlignment lineAlignment,
string layoutId) string? layoutId)
{ {
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: ArrangeLayout", layoutId);
ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId); ArrangeVirtualizingLayout(finalSize, lineAlignment, isWrapping, layoutId);
@ -149,7 +149,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var measureSize = _algorithmCallbacks.Algorithm_GetMeasureSize(index, availableSize, context); var measureSize = _algorithmCallbacks!.Algorithm_GetMeasureSize(index, availableSize, context);
element.Measure(measureSize); element.Measure(measureSize);
var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context); var provisionalArrangeSize = _algorithmCallbacks.Algorithm_GetProvisionalArrangeSize(index, measureSize, element.DesiredSize, context);
_algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context); _algorithmCallbacks.Algorithm_OnElementMeasured(element, index, availableSize, measureSize, element.DesiredSize, provisionalArrangeSize, context);
@ -161,7 +161,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
bool isWrapping, bool isWrapping,
double minItemSpacing, double minItemSpacing,
string layoutId) string? layoutId)
{ {
int anchorIndex = -1; int anchorIndex = -1;
var anchorPosition= new Point(); var anchorPosition= new Point();
@ -170,7 +170,7 @@ namespace Avalonia.Layout
if (!IsVirtualizingContext) if (!IsVirtualizingContext)
{ {
// Non virtualizing host, start generating from the element 0 // Non virtualizing host, start generating from the element 0
anchorIndex = context.ItemCount > 0 ? 0 : -1; anchorIndex = context!.ItemCount > 0 ? 0 : -1;
} }
else else
{ {
@ -183,7 +183,7 @@ namespace Avalonia.Layout
_lastItemSpacing != minItemSpacing || _lastItemSpacing != minItemSpacing ||
_collectionChangePending); _collectionChangePending);
var suggestedAnchorIndex = _context.RecommendedAnchorIndex; var suggestedAnchorIndex = _context!.RecommendedAnchorIndex;
var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 && var isAnchorSuggestionValid = suggestedAnchorIndex >= 0 &&
_elementManager.IsDataIndexRealized(suggestedAnchorIndex); _elementManager.IsDataIndexRealized(suggestedAnchorIndex);
@ -191,10 +191,10 @@ namespace Avalonia.Layout
if (isAnchorSuggestionValid) if (isAnchorSuggestionValid)
{ {
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Using suggested anchor {Anchor}", layoutId, suggestedAnchorIndex);
anchorIndex = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement( anchorIndex = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(
suggestedAnchorIndex, suggestedAnchorIndex,
availableSize, availableSize,
context).Index; context!).Index;
if (_elementManager.IsDataIndexRealized(anchorIndex)) if (_elementManager.IsDataIndexRealized(anchorIndex))
{ {
@ -235,7 +235,7 @@ namespace Avalonia.Layout
// The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
// but not the visible window. In that situation, we still need to produce a valid anchor. // but not the visible window. In that situation, we still need to produce a valid anchor.
var anchorInfo = _algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context); var anchorInfo = _algorithmCallbacks!.Algorithm_GetAnchorForRealizationRect(availableSize, context!);
anchorIndex = anchorInfo.Index; anchorIndex = anchorInfo.Index;
anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset); anchorPosition = _orientation.MinorMajorPoint(0, anchorInfo.Offset);
} }
@ -259,12 +259,12 @@ namespace Avalonia.Layout
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId); Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId} Disconnected Window - throwing away all realized elements", layoutId);
_elementManager.ClearRealizedRange(); _elementManager.ClearRealizedRange();
var anchor = _context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); var anchor = _context!.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
_elementManager.Add(anchor, anchorIndex); _elementManager.Add(anchor, anchorIndex);
} }
var anchorElement = _elementManager.GetRealizedElement(anchorIndex); var anchorElement = _elementManager.GetRealizedElement(anchorIndex);
var desiredSize = MeasureElement(anchorElement, anchorIndex, availableSize, _context); var desiredSize = MeasureElement(anchorElement!, anchorIndex, availableSize, _context!);
var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height); var layoutBounds = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
_elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds); _elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);
@ -296,7 +296,7 @@ namespace Avalonia.Layout
double lineSpacing, double lineSpacing,
int maxItemsPerLine, int maxItemsPerLine,
bool disableVirtualization, bool disableVirtualization,
string layoutId) string? layoutId)
{ {
if (anchorIndex != -1) if (anchorIndex != -1)
{ {
@ -322,7 +322,7 @@ namespace Avalonia.Layout
// Ensure layout element. // Ensure layout element.
_elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId); _elementManager.EnsureElementRealized(direction == GenerateDirection.Forward, currentIndex, layoutId);
var currentElement = _elementManager.GetRealizedElement(currentIndex); var currentElement = _elementManager.GetRealizedElement(currentIndex);
var desiredSize = MeasureElement(currentElement, currentIndex, availableSize, _context); var desiredSize = MeasureElement(currentElement!, currentIndex, availableSize, _context!);
++count; ++count;
// Lay it out. // Lay it out.
@ -333,7 +333,7 @@ namespace Avalonia.Layout
if (direction == GenerateDirection.Forward) if (direction == GenerateDirection.Forward)
{ {
double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize)); double remainingSpace = _orientation.Minor(availableSize) - (_orientation.MinorStart(previousElementBounds) + _orientation.MinorSize(previousElementBounds) + minItemSpacing + _orientation.Minor(desiredSize));
if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
{ {
// No more space in this row. wrap to next row. // No more space in this row. wrap to next row.
_orientation.SetMinorStart(ref currentBounds, 0); _orientation.SetMinorStart(ref currentBounds, 0);
@ -371,7 +371,7 @@ namespace Avalonia.Layout
{ {
// Backward // Backward
double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing); double remainingSpace = _orientation.MinorStart(previousElementBounds) - (_orientation.Minor(desiredSize) + minItemSpacing);
if (countInLine >= maxItemsPerLine || _algorithmCallbacks.Algorithm_ShouldBreakLine(currentIndex, remainingSpace)) if (countInLine >= maxItemsPerLine || _algorithmCallbacks!.Algorithm_ShouldBreakLine(currentIndex, remainingSpace))
{ {
// Does not fit, wrap to the previous row // Does not fit, wrap to the previous row
var availableSizeMinor = _orientation.Minor(availableSize); var availableSizeMinor = _orientation.Minor(availableSize);
@ -432,13 +432,13 @@ namespace Avalonia.Layout
// account for that element in the indices inside the realization window. // account for that element in the indices inside the realization window.
if (direction == GenerateDirection.Forward) if (direction == GenerateDirection.Forward)
{ {
int dataCount = _context.ItemCount; int dataCount = _context!.ItemCount;
_lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1; _lastRealizedDataIndexInsideRealizationWindow = previousIndex == dataCount - 1 ? dataCount - 1 : previousIndex - 1;
_lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow); _lastRealizedDataIndexInsideRealizationWindow = Math.Max(0, _lastRealizedDataIndexInsideRealizationWindow);
} }
else else
{ {
int dataCount = _context.ItemCount; int dataCount = _context!.ItemCount;
_firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1; _firstRealizedDataIndexInsideRealizationWindow = previousIndex == 0 ? 0 : previousIndex + 1;
_firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow); _firstRealizedDataIndexInsideRealizationWindow = Math.Min(dataCount - 1, _firstRealizedDataIndexInsideRealizationWindow);
} }
@ -454,7 +454,7 @@ namespace Avalonia.Layout
{ {
_elementManager.ClearRealizedRange(); _elementManager.ClearRealizedRange();
// FlowLayout requires that the anchor is the first element in the row. // FlowLayout requires that the anchor is the first element in the row.
var internalAnchor = _algorithmCallbacks.Algorithm_GetAnchorForTargetElement(index, availableSize, context); var internalAnchor = _algorithmCallbacks!.Algorithm_GetAnchorForTargetElement(index, availableSize, context);
// No need to set the position of the anchor. // No need to set the position of the anchor.
// (0,0) is fine for now since the extent can // (0,0) is fine for now since the extent can
@ -487,7 +487,7 @@ namespace Avalonia.Layout
} }
else else
{ {
var realizationRect = _context.RealizationRect; var realizationRect = _context!.RealizationRect;
var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index); var elementBounds = _elementManager.GetLayoutBoundsForDataIndex(index);
var elementMajorStart = _orientation.MajorStart(elementBounds); var elementMajorStart = _orientation.MajorStart(elementBounds);
@ -510,11 +510,11 @@ namespace Avalonia.Layout
return shouldContinue; return shouldContinue;
} }
private Rect EstimateExtent(Size availableSize, string layoutId) private Rect EstimateExtent(Size availableSize, string? layoutId)
{ {
ILayoutable firstRealizedElement = null; ILayoutable? firstRealizedElement = null;
Rect firstBounds = new Rect(); Rect firstBounds = new Rect();
ILayoutable lastRealizedElement = null; ILayoutable? lastRealizedElement = null;
Rect lastBounds = new Rect(); Rect lastBounds = new Rect();
int firstDataIndex = -1; int firstDataIndex = -1;
int lastDataIndex = -1; int lastDataIndex = -1;
@ -531,9 +531,9 @@ namespace Avalonia.Layout
lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last); lastBounds = _elementManager.GetLayoutBoundsForRealizedIndex(last);
} }
Rect extent = _algorithmCallbacks.Algorithm_GetExtent( Rect extent = _algorithmCallbacks!.Algorithm_GetExtent(
availableSize, availableSize,
_context, _context!,
firstRealizedElement, firstRealizedElement,
firstDataIndex, firstDataIndex,
firstBounds, firstBounds,
@ -563,7 +563,7 @@ namespace Avalonia.Layout
if (_orientation.MajorStart(currentBounds) != currentLineOffset) if (_orientation.MajorStart(currentBounds) != currentLineOffset)
{ {
// Staring a new line // Staring a new line
_algorithmCallbacks.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context); _algorithmCallbacks!.Algorithm_OnLineArranged(currentDataIndex - countInLine, countInLine, currentLineSize, _context!);
countInLine = 0; countInLine = 0;
currentLineOffset = _orientation.MajorStart(currentBounds); currentLineOffset = _orientation.MajorStart(currentBounds);
currentLineSize = 0; currentLineSize = 0;
@ -575,7 +575,7 @@ namespace Avalonia.Layout
} }
// Raise for the last line. // Raise for the last line.
_algorithmCallbacks.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context); _algorithmCallbacks!.Algorithm_OnLineArranged(_lastRealizedDataIndexInsideRealizationWindow - countInLine + 1, countInLine, currentLineSize, _context!);
} }
} }
} }
@ -584,7 +584,7 @@ namespace Avalonia.Layout
Size finalSize, Size finalSize,
LineAlignment lineAlignment, LineAlignment lineAlignment,
bool isWrapping, bool isWrapping,
string layoutId) string? layoutId)
{ {
// Walk through the realized elements one line at a time and // Walk through the realized elements one line at a time and
// align them, Then call element.Arrange with the arranged bounds. // align them, Then call element.Arrange with the arranged bounds.
@ -636,7 +636,7 @@ namespace Avalonia.Layout
LineAlignment lineAlignment, LineAlignment lineAlignment,
bool isWrapping, bool isWrapping,
Size finalSize, Size finalSize,
string layoutId) string? layoutId)
{ {
for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex) for (int rangeIndex = lineStartIndex; rangeIndex < lineStartIndex + countInLine; ++rangeIndex)
{ {
@ -723,11 +723,11 @@ namespace Avalonia.Layout
{ {
if (IsVirtualizingContext) if (IsVirtualizingContext)
{ {
_context.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y); _context!.LayoutOrigin = new Point(_lastExtent.X, _lastExtent.Y);
} }
} }
public ILayoutable GetElementIfRealized(int dataIndex) public ILayoutable? GetElementIfRealized(int dataIndex)
{ {
if (_elementManager.IsDataIndexRealized(dataIndex)) if (_elementManager.IsDataIndexRealized(dataIndex))
{ {

4
src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs

@ -21,10 +21,10 @@ namespace Avalonia.Layout
Rect Algorithm_GetExtent( Rect Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds); Rect lastRealizedLayoutBounds);
void Algorithm_OnElementMeasured( void Algorithm_OnElementMeasured(

4
src/Avalonia.Layout/LayoutContext.cs

@ -14,7 +14,7 @@ namespace Avalonia.Layout
/// <summary> /// <summary>
/// Gets or sets an object that represents the state of a layout. /// Gets or sets an object that represents the state of a layout.
/// </summary> /// </summary>
public object LayoutState public object? LayoutState
{ {
get => LayoutStateCore; get => LayoutStateCore;
set => LayoutStateCore = value; set => LayoutStateCore = value;
@ -23,6 +23,6 @@ namespace Avalonia.Layout
/// <summary> /// <summary>
/// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext. /// Implements the behavior of <see cref="LayoutState"/> in a derived or custom LayoutContext.
/// </summary> /// </summary>
protected virtual object LayoutStateCore { get; set; } protected virtual object? LayoutStateCore { get; set; }
} }
} }

2
src/Avalonia.Layout/LayoutContextAdapter.cs

@ -16,7 +16,7 @@ namespace Avalonia.Layout
_nonVirtualizingContext = nonVirtualizingContext; _nonVirtualizingContext = nonVirtualizingContext;
} }
protected override object LayoutStateCore protected override object? LayoutStateCore
{ {
get => _nonVirtualizingContext.LayoutState; get => _nonVirtualizingContext.LayoutState;
set => _nonVirtualizingContext.LayoutState = value; set => _nonVirtualizingContext.LayoutState = value;

2
src/Avalonia.Layout/LayoutHelper.cs

@ -101,7 +101,7 @@ namespace Avalonia.Layout
if (result == 0 || double.IsNaN(result) || double.IsInfinity(result)) if (result == 0 || double.IsNaN(result) || double.IsInfinity(result))
{ {
throw new Exception($"Invalid LayoutScaling returned from {visualRoot.GetType()}"); throw new Exception($"Invalid LayoutScaling returned from {visualRoot!.GetType()}");
} }
return result; return result;

1
src/Avalonia.Layout/LayoutQueue.cs

@ -5,6 +5,7 @@ using System.Collections.Generic;
namespace Avalonia.Layout namespace Avalonia.Layout
{ {
internal class LayoutQueue<T> : IReadOnlyCollection<T>, IDisposable internal class LayoutQueue<T> : IReadOnlyCollection<T>, IDisposable
where T : notnull
{ {
private struct Info private struct Info
{ {

2
src/Avalonia.Layout/Layoutable.cs

@ -794,7 +794,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
/// <param name="sender">The sender.</param> /// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
private void LayoutManagedLayoutUpdated(object sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); private void LayoutManagedLayoutUpdated(object? sender, EventArgs e) => _layoutUpdated?.Invoke(this, e);
/// <summary> /// <summary>
/// Tests whether any of a <see cref="Rect"/>'s properties include negative values, /// Tests whether any of a <see cref="Rect"/>'s properties include negative values,

4
src/Avalonia.Layout/NonVirtualizingLayoutContext.cs

@ -12,7 +12,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class NonVirtualizingLayoutContext : LayoutContext public abstract class NonVirtualizingLayoutContext : LayoutContext
{ {
private VirtualizingLayoutContext _contextAdapter; private VirtualizingLayoutContext? _contextAdapter;
/// <summary> /// <summary>
/// Gets the collection of child controls from the container that provides the context. /// Gets the collection of child controls from the container that provides the context.
@ -26,6 +26,6 @@ namespace Avalonia.Layout
protected abstract IReadOnlyList<ILayoutable> ChildrenCore { get; } protected abstract IReadOnlyList<ILayoutable> ChildrenCore { get; }
internal VirtualizingLayoutContext GetVirtualizingContextAdapter() => internal VirtualizingLayoutContext GetVirtualizingContextAdapter() =>
_contextAdapter ?? (_contextAdapter = new LayoutContextAdapter(this)); _contextAdapter ??= new LayoutContextAdapter(this);
} }
} }

22
src/Avalonia.Layout/StackLayout.cs

@ -78,10 +78,10 @@ namespace Avalonia.Layout
internal Rect GetExtent( internal Rect GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -89,7 +89,7 @@ namespace Avalonia.Layout
// Constants // Constants
int itemsCount = context.ItemCount; int itemsCount = context.ItemCount;
var stackState = (StackLayoutState)context.LayoutState; var stackState = (StackLayoutState)context.LayoutState!;
double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing;
_orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds);
@ -131,7 +131,7 @@ namespace Avalonia.Layout
{ {
if (context is VirtualizingLayoutContext virtualContext) if (context is VirtualizingLayoutContext virtualContext)
{ {
var stackState = (StackLayoutState)virtualContext.LayoutState; var stackState = (StackLayoutState)virtualContext.LayoutState!;
var provisionalArrangeSizeWinRt = provisionalArrangeSize; var provisionalArrangeSizeWinRt = provisionalArrangeSize;
stackState.OnElementMeasured( stackState.OnElementMeasured(
index, index,
@ -177,7 +177,7 @@ namespace Avalonia.Layout
if (targetIndex >= 0 && targetIndex < itemsCount) if (targetIndex >= 0 && targetIndex < itemsCount)
{ {
index = targetIndex; index = targetIndex;
var state = (StackLayoutState)context.LayoutState; var state = (StackLayoutState)context.LayoutState!;
double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent);
} }
@ -188,10 +188,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -234,7 +234,7 @@ namespace Avalonia.Layout
if (itemsCount > 0) if (itemsCount > 0)
{ {
var realizationRect = context.RealizationRect; var realizationRect = context.RealizationRect;
var state = (StackLayoutState)context.LayoutState; var state = (StackLayoutState)context.LayoutState!;
var lastExtent = state.FlowAlgorithm.LastExtent; var lastExtent = state.FlowAlgorithm.LastExtent;
double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing;
@ -279,13 +279,13 @@ namespace Avalonia.Layout
protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
{ {
var stackState = (StackLayoutState)context.LayoutState; var stackState = (StackLayoutState)context.LayoutState!;
stackState.UninitializeForContext(context); stackState.UninitializeForContext(context);
} }
protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) protected internal override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
{ {
((StackLayoutState)context.LayoutState).OnMeasureStart(); ((StackLayoutState)context.LayoutState!).OnMeasureStart();
var desiredSize = GetFlowAlgorithm(context).Measure( var desiredSize = GetFlowAlgorithm(context).Measure(
availableSize, availableSize,
@ -358,6 +358,6 @@ namespace Avalonia.Layout
private void InvalidateLayout() => InvalidateMeasure(); private void InvalidateLayout() => InvalidateMeasure();
private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState).FlowAlgorithm; private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm;
} }
} }

26
src/Avalonia.Layout/UniformGridLayout.cs

@ -258,7 +258,7 @@ namespace Avalonia.Layout
Size availableSize, Size availableSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
} }
@ -268,7 +268,7 @@ namespace Avalonia.Layout
Size desiredSize, Size desiredSize,
VirtualizingLayoutContext context) VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight); return new Size(gridState.EffectiveItemWidth, gridState.EffectiveItemHeight);
} }
@ -285,7 +285,7 @@ namespace Avalonia.Layout
var realizationRect = context.RealizationRect; var realizationRect = context.RealizationRect;
if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0) if (itemsCount > 0 && _orientation.MajorSize(realizationRect) > 0)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
var lastExtent = gridState.FlowAlgorithm.LastExtent; var lastExtent = gridState.FlowAlgorithm.LastExtent;
var itemsPerLine = Math.Min( // note use of unsigned ints var itemsPerLine = Math.Min( // note use of unsigned ints
Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))), Math.Max(1u, (uint)(_orientation.Minor(availableSize) / GetMinorSizeWithSpacing(context))),
@ -324,7 +324,7 @@ namespace Avalonia.Layout
Math.Max(1u, _maximumRowsOrColumns)); Math.Max(1u, _maximumRowsOrColumns));
int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine; int indexOfFirstInLine = (targetIndex / itemsPerLine) * itemsPerLine;
index = indexOfFirstInLine; index = indexOfFirstInLine;
var state = context.LayoutState as UniformGridLayoutState; var state = (UniformGridLayoutState)context.LayoutState!;
offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context)); offset = _orientation.MajorStart(GetLayoutRectForDataIndex(availableSize, indexOfFirstInLine, state.FlowAlgorithm.LastExtent, context));
} }
@ -338,10 +338,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent( Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize, Size availableSize,
VirtualizingLayoutContext context, VirtualizingLayoutContext context,
ILayoutable firstRealized, ILayoutable? firstRealized,
int firstRealizedItemIndex, int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds, Rect firstRealizedLayoutBounds,
ILayoutable lastRealized, ILayoutable? lastRealized,
int lastRealizedItemIndex, int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds) Rect lastRealizedLayoutBounds)
{ {
@ -421,7 +421,7 @@ namespace Avalonia.Layout
protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context) protected internal override void UninitializeForContextCore(VirtualizingLayoutContext context)
{ {
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.UninitializeForContext(context); gridState.UninitializeForContext(context);
} }
@ -429,7 +429,7 @@ namespace Avalonia.Layout
{ {
// Set the width and height on the grid state. If the user already set them then use the preset. // Set the width and height on the grid state. If the user already set them then use the preset.
// If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items. // If not, we have to measure the first element and get back a size which we're going to be using for the rest of the items.
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns); gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns);
var desiredSize = GetFlowAlgorithm(context).Measure( var desiredSize = GetFlowAlgorithm(context).Measure(
@ -467,7 +467,7 @@ namespace Avalonia.Layout
// Always invalidate layout to keep the view accurate. // Always invalidate layout to keep the view accurate.
InvalidateLayout(); InvalidateLayout();
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
gridState.ClearElementOnDataSourceChange(context, args); gridState.ClearElementOnDataSourceChange(context, args);
} }
@ -517,7 +517,7 @@ namespace Avalonia.Layout
private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context) private double GetMinorSizeWithSpacing(VirtualizingLayoutContext context)
{ {
var minItemSpacing = MinItemSpacing; var minItemSpacing = MinItemSpacing;
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return _orientation.ScrollOrientation == ScrollOrientation.Vertical? return _orientation.ScrollOrientation == ScrollOrientation.Vertical?
gridState.EffectiveItemWidth + minItemSpacing : gridState.EffectiveItemWidth + minItemSpacing :
gridState.EffectiveItemHeight + minItemSpacing; gridState.EffectiveItemHeight + minItemSpacing;
@ -526,7 +526,7 @@ namespace Avalonia.Layout
private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context) private double GetMajorSizeWithSpacing(VirtualizingLayoutContext context)
{ {
var lineSpacing = LineSpacing; var lineSpacing = LineSpacing;
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
return _orientation.ScrollOrientation == ScrollOrientation.Vertical ? return _orientation.ScrollOrientation == ScrollOrientation.Vertical ?
gridState.EffectiveItemHeight + lineSpacing : gridState.EffectiveItemHeight + lineSpacing :
gridState.EffectiveItemWidth + lineSpacing; gridState.EffectiveItemWidth + lineSpacing;
@ -544,7 +544,7 @@ namespace Avalonia.Layout
int rowIndex = (int)(index / itemsPerLine); int rowIndex = (int)(index / itemsPerLine);
int indexInRow = index - (rowIndex * itemsPerLine); int indexInRow = index - (rowIndex * itemsPerLine);
var gridState = (UniformGridLayoutState)context.LayoutState; var gridState = (UniformGridLayoutState)context.LayoutState!;
Rect bounds = _orientation.MinorMajorRect( Rect bounds = _orientation.MinorMajorRect(
indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent), indexInRow * GetMinorSizeWithSpacing(context) + _orientation.MinorStart(lastExtent),
rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent), rowIndex * GetMajorSizeWithSpacing(context) + _orientation.MajorStart(lastExtent),
@ -556,6 +556,6 @@ namespace Avalonia.Layout
private void InvalidateLayout() => InvalidateMeasure(); private void InvalidateLayout() => InvalidateMeasure();
private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState).FlowAlgorithm; private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm;
} }
} }

2
src/Avalonia.Layout/UniformGridLayoutState.cs

@ -18,7 +18,7 @@ namespace Avalonia.Layout
// If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it. // If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it.
// If we are responsible for clearing element 0 we keep m_cachedFirstElement valid. // If we are responsible for clearing element 0 we keep m_cachedFirstElement valid.
// If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm. // If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm.
private ILayoutable _cachedFirstElement; private ILayoutable? _cachedFirstElement;
internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm(); internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
internal double EffectiveItemWidth { get; private set; } internal double EffectiveItemWidth { get; private set; }

5
src/Avalonia.Layout/Utils/ListUtils.cs

@ -23,10 +23,5 @@ namespace Avalonia.Layout.Utils
list.AddRange(Enumerable.Repeat(value, size - cur)); list.AddRange(Enumerable.Repeat(value, size - cur));
} }
} }
public static void Resize<T>(this List<T> list, int count)
{
Resize(list, count, default);
}
} }
} }

4
src/Avalonia.Layout/VirtualLayoutContextAdapter.cs

@ -6,14 +6,14 @@ namespace Avalonia.Layout
public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext
{ {
private readonly VirtualizingLayoutContext _virtualizingContext; private readonly VirtualizingLayoutContext _virtualizingContext;
private ChildrenCollection _children; private ChildrenCollection? _children;
public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext) public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext)
{ {
_virtualizingContext = virtualizingContext; _virtualizingContext = virtualizingContext;
} }
protected override object LayoutStateCore protected override object? LayoutStateCore
{ {
get => _virtualizingContext.LayoutState; get => _virtualizingContext.LayoutState;
set => _virtualizingContext.LayoutState = value; set => _virtualizingContext.LayoutState = value;

2
src/Avalonia.Layout/VirtualizingLayoutContext.cs

@ -43,7 +43,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public abstract class VirtualizingLayoutContext : LayoutContext public abstract class VirtualizingLayoutContext : LayoutContext
{ {
private NonVirtualizingLayoutContext _contextAdapter; private NonVirtualizingLayoutContext? _contextAdapter;
/// <summary> /// <summary>
/// Gets the number of items in the data. /// Gets the number of items in the data.

2
src/Avalonia.Layout/WrapLayout/UvMeasure.cs

@ -27,7 +27,7 @@ namespace Avalonia.Layout
} }
} }
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
if (obj is UvMeasure measure) if (obj is UvMeasure measure)
{ {

2
src/Avalonia.Layout/WrapLayout/WrapItem.cs

@ -20,6 +20,6 @@ namespace Avalonia.Layout
public UvMeasure? Position { get; internal set; } public UvMeasure? Position { get; internal set; }
public ILayoutable Element { get; internal set; } public ILayoutable? Element { get; internal set; }
} }
} }

6
src/Avalonia.Layout/WrapLayout/WrapLayout.cs

@ -88,7 +88,7 @@ namespace Avalonia.Layout
/// <inheritdoc /> /// <inheritdoc />
protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args) protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs args)
{ {
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
switch (args.Action) switch (args.Action)
{ {
@ -126,7 +126,7 @@ namespace Avalonia.Layout
var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
var position = UvMeasure.Zero; var position = UvMeasure.Zero;
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
if (state.Orientation != Orientation) if (state.Orientation != Orientation)
{ {
state.SetOrientation(Orientation); state.SetOrientation(Orientation);
@ -261,7 +261,7 @@ namespace Avalonia.Layout
var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
var realizationBounds = new UvBounds(Orientation, context.RealizationRect); var realizationBounds = new UvBounds(Orientation, context.RealizationRect);
var state = (WrapLayoutState)context.LayoutState; var state = (WrapLayoutState)context.LayoutState!;
bool Arrange(WrapItem item, bool isLast = false) bool Arrange(WrapItem item, bool isLast = false)
{ {
if (item.Measure.HasValue == false) if (item.Measure.HasValue == false)

9
src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs

@ -62,8 +62,11 @@ namespace Avalonia.Layout
internal void SetOrientation(Orientation orientation) internal void SetOrientation(Orientation orientation)
{ {
foreach (var item in _items.Where(i => i.Measure.HasValue)) foreach (var item in _items)
{ {
if (!item.Measure.HasValue)
continue;
UvMeasure measure = item.Measure.Value; UvMeasure measure = item.Measure.Value;
double v = measure.V; double v = measure.V;
measure.V = measure.U; measure.V = measure.U;
@ -120,10 +123,10 @@ namespace Avalonia.Layout
} }
lastPosition = item.Position; lastPosition = item.Position;
maxV = Math.Max(maxV, item.Measure.Value.V); maxV = Math.Max(maxV, item.Measure!.Value.V);
} }
double totalHeight = lastPosition.Value.V + maxV; double totalHeight = lastPosition!.Value.V + maxV;
if (calculateAverage) if (calculateAverage)
{ {
return (totalHeight / itemCount) * _context.ItemCount; return (totalHeight / itemCount) * _context.ItemCount;

2
src/Avalonia.Native/Avalonia.Native.csproj

@ -4,7 +4,7 @@
<PackAvaloniaNative Condition="'$(PackAvaloniaNative)' == ''">$([MSBuild]::IsOSPlatform(OSX))</PackAvaloniaNative> <PackAvaloniaNative Condition="'$(PackAvaloniaNative)' == ''">$([MSBuild]::IsOSPlatform(OSX))</PackAvaloniaNative>
<IsPackable>$(PackAvaloniaNative)</IsPackable> <IsPackable>$(PackAvaloniaNative)</IsPackable>
<IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable> <IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

7
src/Avalonia.Native/AvaloniaNativeMenuExporter.cs

@ -142,6 +142,13 @@ namespace Avalonia.Native
private void DoLayoutReset(bool forceUpdate = false) private void DoLayoutReset(bool forceUpdate = false)
{ {
var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
if (macOpts != null && macOpts.DisableNativeMenus)
{
return;
}
if (_resetQueued || forceUpdate) if (_resetQueued || forceUpdate)
{ {
_resetQueued = false; _resetQueued = false;

5
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -73,5 +73,10 @@ namespace Avalonia
/// You can prevent Avalonia from adding those items to the OSX Application Menu with this property. The default value is false. /// You can prevent Avalonia from adding those items to the OSX Application Menu with this property. The default value is false.
/// </summary> /// </summary>
public bool DisableDefaultApplicationMenuItems { get; set; } public bool DisableDefaultApplicationMenuItems { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the native macOS menu bar will be enabled for the application.
/// </summary>
public bool DisableNativeMenus { get; set; }
} }
} }

2
src/Avalonia.OpenGL/Avalonia.OpenGL.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

2
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<PackageId>Avalonia.ReactiveUI</PackageId> <PackageId>Avalonia.ReactiveUI</PackageId>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
</PropertyGroup> </PropertyGroup>

2
src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<DefineConstants>AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)</DefineConstants> <DefineConstants>AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)</DefineConstants>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>

3
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AssemblyName>Avalonia.Styling</AssemblyName> <AssemblyName>Avalonia.Styling</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
</PropertyGroup> </PropertyGroup>
@ -9,4 +9,5 @@
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

12
src/Avalonia.Styling/Controls/ChildNameScope.cs

@ -15,20 +15,20 @@ namespace Avalonia.Controls
public void Register(string name, object element) => _inner.Register(name, element); public void Register(string name, object element) => _inner.Register(name, element);
public SynchronousCompletionAsyncResult<object> FindAsync(string name) public SynchronousCompletionAsyncResult<object?> FindAsync(string name)
{ {
var found = Find(name); var found = Find(name);
if (found != null) if (found != null)
return new SynchronousCompletionAsyncResult<object>(found); return new SynchronousCompletionAsyncResult<object?>(found);
// Not found and both current and parent scope are in completed state // Not found and both current and parent scope are in completed state
if(IsCompleted) if(IsCompleted)
return new SynchronousCompletionAsyncResult<object>(null); return new SynchronousCompletionAsyncResult<object?>(null);
return DoFindAsync(name); return DoFindAsync(name);
} }
public SynchronousCompletionAsyncResult<object> DoFindAsync(string name) public SynchronousCompletionAsyncResult<object?> DoFindAsync(string name)
{ {
var src = new SynchronousCompletionAsyncResultSource<object>(); var src = new SynchronousCompletionAsyncResultSource<object?>();
void ParentSearch() void ParentSearch()
{ {
@ -56,7 +56,7 @@ namespace Avalonia.Controls
return src.AsyncResult; return src.AsyncResult;
} }
public object Find(string name) public object? Find(string name)
{ {
var found = _inner.Find(name); var found = _inner.Find(name);
if (found != null) if (found != null)

4
src/Avalonia.Styling/Controls/INameScope.cs

@ -22,14 +22,14 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The element, or null if the name was not found.</returns> /// <returns>The element, or null if the name was not found.</returns>
SynchronousCompletionAsyncResult<object> FindAsync(string name); SynchronousCompletionAsyncResult<object?> FindAsync(string name);
/// <summary> /// <summary>
/// Finds a named element in the name scope, returns immediately, doesn't traverse the name scope stack /// Finds a named element in the name scope, returns immediately, doesn't traverse the name scope stack
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The element, or null if the name was not found.</returns> /// <returns>The element, or null if the name was not found.</returns>
object Find(string name); object? Find(string name);
/// <summary> /// <summary>
/// Marks the name scope as completed, no further registrations will be allowed /// Marks the name scope as completed, no further registrations will be allowed

30
src/Avalonia.Styling/Controls/NameScope.cs

@ -22,8 +22,8 @@ namespace Avalonia.Controls
private readonly Dictionary<string, object> _inner = new Dictionary<string, object>(); private readonly Dictionary<string, object> _inner = new Dictionary<string, object>();
private readonly Dictionary<string, SynchronousCompletionAsyncResultSource<object>> _pendingSearches = private readonly Dictionary<string, SynchronousCompletionAsyncResultSource<object?>> _pendingSearches =
new Dictionary<string, SynchronousCompletionAsyncResultSource<object>>(); new Dictionary<string, SynchronousCompletionAsyncResultSource<object?>>();
/// <summary> /// <summary>
/// Gets the value of the attached <see cref="NameScopeProperty"/> on a styled element. /// Gets the value of the attached <see cref="NameScopeProperty"/> on a styled element.
@ -32,7 +32,7 @@ namespace Avalonia.Controls
/// <returns>The value of the NameScope attached property.</returns> /// <returns>The value of the NameScope attached property.</returns>
public static INameScope GetNameScope(StyledElement styled) public static INameScope GetNameScope(StyledElement styled)
{ {
Contract.Requires<ArgumentNullException>(styled != null); _ = styled ?? throw new ArgumentNullException(nameof(styled));
return styled.GetValue(NameScopeProperty); return styled.GetValue(NameScopeProperty);
} }
@ -44,7 +44,7 @@ namespace Avalonia.Controls
/// <param name="value">The value to set.</param> /// <param name="value">The value to set.</param>
public static void SetNameScope(StyledElement styled, INameScope value) public static void SetNameScope(StyledElement styled, INameScope value)
{ {
Contract.Requires<ArgumentNullException>(styled != null); _ = styled ?? throw new ArgumentNullException(nameof(styled));
styled.SetValue(NameScopeProperty, value); styled.SetValue(NameScopeProperty, value);
} }
@ -54,12 +54,11 @@ namespace Avalonia.Controls
{ {
if (IsCompleted) if (IsCompleted)
throw new InvalidOperationException("NameScope is completed, no further registrations are allowed"); throw new InvalidOperationException("NameScope is completed, no further registrations are allowed");
Contract.Requires<ArgumentNullException>(name != null);
Contract.Requires<ArgumentNullException>(element != null);
object existing; _ = name ?? throw new ArgumentNullException(nameof(name));
_ = element ?? throw new ArgumentNullException(nameof(element));
if (_inner.TryGetValue(name, out existing)) if (_inner.TryGetValue(name, out var existing))
{ {
if (existing != element) if (existing != element)
{ {
@ -77,27 +76,26 @@ namespace Avalonia.Controls
} }
} }
public SynchronousCompletionAsyncResult<object> FindAsync(string name) public SynchronousCompletionAsyncResult<object?> FindAsync(string name)
{ {
var found = Find(name); var found = Find(name);
if (found != null) if (found != null)
return new SynchronousCompletionAsyncResult<object>(found); return new SynchronousCompletionAsyncResult<object?>(found);
if (IsCompleted) if (IsCompleted)
return new SynchronousCompletionAsyncResult<object>((object)null); return new SynchronousCompletionAsyncResult<object?>(null);
if (!_pendingSearches.TryGetValue(name, out var tcs)) if (!_pendingSearches.TryGetValue(name, out var tcs))
// We are intentionally running continuations synchronously here // We are intentionally running continuations synchronously here
_pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource<object>(); _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource<object?>();
return tcs.AsyncResult; return tcs.AsyncResult;
} }
/// <inheritdoc /> /// <inheritdoc />
public object Find(string name) public object? Find(string name)
{ {
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
object result; _inner.TryGetValue(name, out var result);
_inner.TryGetValue(name, out result);
return result; return result;
} }

28
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@ -17,11 +17,11 @@ namespace Avalonia.Controls
/// <param name="nameScope">The name scope.</param> /// <param name="nameScope">The name scope.</param>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The named element or null if not found.</returns> /// <returns>The named element or null if not found.</returns>
public static T Find<T>(this INameScope nameScope, string name) public static T? Find<T>(this INameScope nameScope, string name)
where T : class where T : class
{ {
Contract.Requires<ArgumentNullException>(nameScope != null); _ = nameScope ?? throw new ArgumentNullException(nameof(nameScope));
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
var result = nameScope.Find(name); var result = nameScope.Find(name);
@ -31,7 +31,7 @@ namespace Avalonia.Controls
$"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
} }
return (T)result; return (T?)result;
} }
/// <summary> /// <summary>
@ -41,11 +41,11 @@ namespace Avalonia.Controls
/// <param name="anchor">The control to take the name scope from.</param> /// <param name="anchor">The control to take the name scope from.</param>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The named element or null if not found.</returns> /// <returns>The named element or null if not found.</returns>
public static T Find<T>(this ILogical anchor, string name) public static T? Find<T>(this ILogical anchor, string name)
where T : class where T : class
{ {
Contract.Requires<ArgumentNullException>(anchor != null); _ = anchor ?? throw new ArgumentNullException(nameof(anchor));
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
var styledAnchor = anchor as StyledElement; var styledAnchor = anchor as StyledElement;
if (styledAnchor == null) if (styledAnchor == null)
return null; return null;
@ -64,8 +64,8 @@ namespace Avalonia.Controls
public static T Get<T>(this INameScope nameScope, string name) public static T Get<T>(this INameScope nameScope, string name)
where T : class where T : class
{ {
Contract.Requires<ArgumentNullException>(nameScope != null); _ = nameScope ?? throw new ArgumentNullException(nameof(nameScope));
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
var result = nameScope.Find(name); var result = nameScope.Find(name);
@ -94,9 +94,9 @@ namespace Avalonia.Controls
public static T Get<T>(this ILogical anchor, string name) public static T Get<T>(this ILogical anchor, string name)
where T : class where T : class
{ {
Contract.Requires<ArgumentNullException>(anchor != null); _ = anchor ?? throw new ArgumentNullException(nameof(anchor));
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope((StyledElement)anchor); var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope((StyledElement)anchor);
if (nameScope == null) if (nameScope == null)
throw new InvalidOperationException( throw new InvalidOperationException(
@ -105,9 +105,9 @@ namespace Avalonia.Controls
return nameScope.Get<T>(name); return nameScope.Get<T>(name);
} }
public static INameScope FindNameScope(this ILogical control) public static INameScope? FindNameScope(this ILogical control)
{ {
Contract.Requires<ArgumentNullException>(control != null); _ = control ?? throw new ArgumentNullException(nameof(control));
var scope = control.GetSelfAndLogicalAncestors() var scope = control.GetSelfAndLogicalAncestors()
.OfType<StyledElement>() .OfType<StyledElement>()

8
src/Avalonia.Styling/Controls/NameScopeLocator.cs

@ -11,9 +11,9 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <param name="scope">The scope relative from which the object should be resolved.</param> /// <param name="scope">The scope relative from which the object should be resolved.</param>
/// <param name="name">The name of the object to find.</param> /// <param name="name">The name of the object to find.</param>
public static IObservable<object> Track(INameScope scope, string name) public static IObservable<object?> Track(INameScope scope, string name)
{ {
return new NeverEndingSynchronousCompletionAsyncResultObservable<object>(scope.FindAsync(name)); return new NeverEndingSynchronousCompletionAsyncResultObservable<object?>(scope.FindAsync(name));
} }
// This class is implemented in such weird way because for some reason // This class is implemented in such weird way because for some reason
@ -22,7 +22,7 @@ namespace Avalonia.Controls
private class NeverEndingSynchronousCompletionAsyncResultObservable<T> : IObservable<T> private class NeverEndingSynchronousCompletionAsyncResultObservable<T> : IObservable<T>
{ {
private T _value; private T? _value;
private SynchronousCompletionAsyncResult<T>? _asyncResult; private SynchronousCompletionAsyncResult<T>? _asyncResult;
public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult<T> task) public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult<T> task)
@ -47,7 +47,7 @@ namespace Avalonia.Controls
observer.OnNext(_asyncResult.Value.GetResult()); observer.OnNext(_asyncResult.Value.GetResult());
}); });
else else
observer.OnNext(_value); observer.OnNext(_value!);
return Disposable.Empty; return Disposable.Empty;
} }

2
src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs

@ -12,7 +12,7 @@ namespace Avalonia.Controls
/// <param name="value">True to add the pseudoclass or false to remove.</param> /// <param name="value">True to add the pseudoclass or false to remove.</param>
public static void Set(this IPseudoClasses classes, string name, bool value) public static void Set(this IPseudoClasses classes, string name, bool value)
{ {
Contract.Requires<ArgumentNullException>(classes != null); _ = classes ?? throw new ArgumentNullException(nameof(classes));
if (value) if (value)
{ {

2
src/Avalonia.Styling/Controls/ResourceDictionary.cs

@ -177,7 +177,7 @@ namespace Avalonia.Controls
} }
} }
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
} }

6
src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs

@ -109,7 +109,7 @@ namespace Avalonia.Controls
observer.OnNext(Convert(_target.FindResource(_key))); observer.OnNext(Convert(_target.FindResource(_key)));
} }
private void ResourcesChanged(object sender, ResourcesChangedEventArgs e) private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e)
{ {
PublishNext(Convert(_target.FindResource(_key))); PublishNext(Convert(_target.FindResource(_key)));
} }
@ -159,7 +159,7 @@ namespace Avalonia.Controls
} }
} }
private void OwnerChanged(object sender, EventArgs e) private void OwnerChanged(object? sender, EventArgs e)
{ {
if (_owner is object) if (_owner is object)
{ {
@ -176,7 +176,7 @@ namespace Avalonia.Controls
PublishNext(); PublishNext();
} }
private void ResourcesChanged(object sender, ResourcesChangedEventArgs e) private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e)
{ {
PublishNext(); PublishNext();
} }

2
src/Avalonia.Styling/INamed.cs

@ -8,6 +8,6 @@ namespace Avalonia
/// <summary> /// <summary>
/// Gets the element name. /// Gets the element name.
/// </summary> /// </summary>
string Name { get; } string? Name { get; }
} }
} }

16
src/Avalonia.Styling/LogicalTree/ControlLocator.cs

@ -9,19 +9,19 @@ namespace Avalonia.LogicalTree
/// </summary> /// </summary>
public static class ControlLocator public static class ControlLocator
{ {
public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null) public static IObservable<ILogical?> Track(ILogical relativeTo, int ancestorLevel, Type? ancestorType = null)
{ {
return new ControlTracker(relativeTo, ancestorLevel, ancestorType); return new ControlTracker(relativeTo, ancestorLevel, ancestorType);
} }
private class ControlTracker : LightweightObservableBase<ILogical> private class ControlTracker : LightweightObservableBase<ILogical?>
{ {
private readonly ILogical _relativeTo; private readonly ILogical _relativeTo;
private readonly int _ancestorLevel; private readonly int _ancestorLevel;
private readonly Type _ancestorType; private readonly Type? _ancestorType;
private ILogical _value; private ILogical? _value;
public ControlTracker(ILogical relativeTo, int ancestorLevel, Type ancestorType) public ControlTracker(ILogical relativeTo, int ancestorLevel, Type? ancestorType)
{ {
_relativeTo = relativeTo; _relativeTo = relativeTo;
_ancestorLevel = ancestorLevel; _ancestorLevel = ancestorLevel;
@ -43,18 +43,18 @@ namespace Avalonia.LogicalTree
_value = null; _value = null;
} }
protected override void Subscribed(IObserver<ILogical> observer, bool first) protected override void Subscribed(IObserver<ILogical?> observer, bool first)
{ {
observer.OnNext(_value); observer.OnNext(_value);
} }
private void Attached(object sender, LogicalTreeAttachmentEventArgs e) private void Attached(object? sender, LogicalTreeAttachmentEventArgs e)
{ {
Update(); Update();
PublishNext(_value); PublishNext(_value);
} }
private void Detached(object sender, LogicalTreeAttachmentEventArgs e) private void Detached(object? sender, LogicalTreeAttachmentEventArgs e)
{ {
_value = null; _value = null;
PublishNext(null); PublishNext(null);

6
src/Avalonia.Styling/LogicalTree/ILogical.cs

@ -12,12 +12,12 @@ namespace Avalonia.LogicalTree
/// <summary> /// <summary>
/// Raised when the control is attached to a rooted logical tree. /// Raised when the control is attached to a rooted logical tree.
/// </summary> /// </summary>
event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree; event EventHandler<LogicalTreeAttachmentEventArgs>? AttachedToLogicalTree;
/// <summary> /// <summary>
/// Raised when the control is detached from a rooted logical tree. /// Raised when the control is detached from a rooted logical tree.
/// </summary> /// </summary>
event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree; event EventHandler<LogicalTreeAttachmentEventArgs>? DetachedFromLogicalTree;
/// <summary> /// <summary>
/// Gets a value indicating whether the element is attached to a rooted logical tree. /// Gets a value indicating whether the element is attached to a rooted logical tree.
@ -27,7 +27,7 @@ namespace Avalonia.LogicalTree
/// <summary> /// <summary>
/// Gets the logical parent. /// Gets the logical parent.
/// </summary> /// </summary>
ILogical LogicalParent { get; } ILogical? LogicalParent { get; }
/// <summary> /// <summary>
/// Gets the logical children. /// Gets the logical children.

26
src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs

@ -15,14 +15,14 @@ namespace Avalonia.LogicalTree
/// <returns>The logical's ancestors.</returns> /// <returns>The logical's ancestors.</returns>
public static IEnumerable<ILogical> GetLogicalAncestors(this ILogical logical) public static IEnumerable<ILogical> GetLogicalAncestors(this ILogical logical)
{ {
Contract.Requires<ArgumentNullException>(logical != null); _ = logical ?? throw new ArgumentNullException(nameof(logical));
logical = logical.LogicalParent; ILogical? l = logical.LogicalParent;
while (logical != null) while (l != null)
{ {
yield return logical; yield return l;
logical = logical.LogicalParent; l = l.LogicalParent;
} }
} }
@ -48,14 +48,14 @@ namespace Avalonia.LogicalTree
/// <param name="logical">The logical.</param> /// <param name="logical">The logical.</param>
/// <param name="includeSelf">If given logical should be included in search.</param> /// <param name="includeSelf">If given logical should be included in search.</param>
/// <returns>First ancestor of given type.</returns> /// <returns>First ancestor of given type.</returns>
public static T FindLogicalAncestorOfType<T>(this ILogical logical, bool includeSelf = false) where T : class public static T? FindLogicalAncestorOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
{ {
if (logical is null) if (logical is null)
{ {
return null; return null;
} }
ILogical parent = includeSelf ? logical : logical.LogicalParent; var parent = includeSelf ? logical : logical.LogicalParent;
while (parent != null) while (parent != null)
{ {
@ -120,7 +120,7 @@ namespace Avalonia.LogicalTree
/// <param name="logical">The logical.</param> /// <param name="logical">The logical.</param>
/// <param name="includeSelf">If given logical should be included in search.</param> /// <param name="includeSelf">If given logical should be included in search.</param>
/// <returns>First descendant of given type.</returns> /// <returns>First descendant of given type.</returns>
public static T FindLogicalDescendantOfType<T>(this ILogical logical, bool includeSelf = false) where T : class public static T? FindLogicalDescendantOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
{ {
if (logical is null) if (logical is null)
{ {
@ -140,7 +140,7 @@ namespace Avalonia.LogicalTree
/// </summary> /// </summary>
/// <param name="logical">The logical.</param> /// <param name="logical">The logical.</param>
/// <returns>The parent, or null if the logical is unparented.</returns> /// <returns>The parent, or null if the logical is unparented.</returns>
public static ILogical GetLogicalParent(this ILogical logical) public static ILogical? GetLogicalParent(this ILogical logical)
{ {
return logical.LogicalParent; return logical.LogicalParent;
} }
@ -153,7 +153,7 @@ namespace Avalonia.LogicalTree
/// <returns> /// <returns>
/// The parent, or null if the logical is unparented or its parent is not of type <typeparamref name="T"/>. /// The parent, or null if the logical is unparented or its parent is not of type <typeparamref name="T"/>.
/// </returns> /// </returns>
public static T GetLogicalParent<T>(this ILogical logical) where T : class public static T? GetLogicalParent<T>(this ILogical logical) where T : class
{ {
return logical.LogicalParent as T; return logical.LogicalParent as T;
} }
@ -165,7 +165,7 @@ namespace Avalonia.LogicalTree
/// <returns>The logical siblings.</returns> /// <returns>The logical siblings.</returns>
public static IEnumerable<ILogical> GetLogicalSiblings(this ILogical logical) public static IEnumerable<ILogical> GetLogicalSiblings(this ILogical logical)
{ {
ILogical parent = logical.LogicalParent; var parent = logical.LogicalParent;
if (parent != null) if (parent != null)
{ {
@ -187,7 +187,7 @@ namespace Avalonia.LogicalTree
/// </returns> /// </returns>
public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target)
{ {
ILogical current = target?.LogicalParent; var current = target?.LogicalParent;
while (current != null) while (current != null)
{ {
@ -202,7 +202,7 @@ namespace Avalonia.LogicalTree
return false; return false;
} }
private static T FindDescendantOfTypeCore<T>(ILogical logical) where T : class private static T? FindDescendantOfTypeCore<T>(ILogical logical) where T : class
{ {
var logicalChildren = logical.LogicalChildren; var logicalChildren = logical.LogicalChildren;
var logicalChildrenCount = logicalChildren.Count; var logicalChildrenCount = logicalChildren.Count;

7
src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs

@ -19,11 +19,8 @@ namespace Avalonia.LogicalTree
ILogical source, ILogical source,
ILogical parent) ILogical parent)
{ {
Contract.Requires<ArgumentNullException>(root != null); Root = root ?? throw new ArgumentNullException(nameof(root));
Contract.Requires<ArgumentNullException>(source != null); Source = source ?? throw new ArgumentNullException(nameof(source));
Root = root;
Source = source;
Parent = parent; Parent = parent;
} }

18
src/Avalonia.Styling/StyledElement.cs

@ -427,7 +427,7 @@ namespace Avalonia
if (_logicalRoot != null) if (_logicalRoot != null)
{ {
var e = new LogicalTreeAttachmentEventArgs(_logicalRoot, this, old); var e = new LogicalTreeAttachmentEventArgs(_logicalRoot, this, old!);
OnDetachedFromLogicalTreeCore(e); OnDetachedFromLogicalTreeCore(e);
} }
@ -435,7 +435,7 @@ namespace Avalonia
if (newRoot is object) if (newRoot is object)
{ {
var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent); var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent!);
OnAttachedToLogicalTreeCore(e); OnAttachedToLogicalTreeCore(e);
} }
else if (parent is null) else if (parent is null)
@ -495,21 +495,21 @@ namespace Avalonia
DetachStylesFromThisAndDescendents(allStyles); DetachStylesFromThisAndDescendents(allStyles);
} }
protected virtual void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) protected virtual void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems); SetLogicalParent(e.NewItems!);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems); ClearLogicalParent(e.OldItems!);
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems); ClearLogicalParent(e.OldItems!);
SetLogicalParent(e.NewItems); SetLogicalParent(e.NewItems!);
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
@ -729,7 +729,7 @@ namespace Avalonia
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var logical = (ILogical) children[i]; var logical = (ILogical) children[i]!;
if (logical.LogicalParent is null) if (logical.LogicalParent is null)
{ {
@ -744,7 +744,7 @@ namespace Avalonia
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var logical = (ILogical) children[i]; var logical = (ILogical) children[i]!;
if (logical.LogicalParent == this) if (logical.LogicalParent == this)
{ {

2
src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs

@ -37,7 +37,7 @@ namespace Avalonia.Styling.Activators
_provider.ChildIndexChanged -= ChildIndexChanged; _provider.ChildIndexChanged -= ChildIndexChanged;
} }
private void ChildIndexChanged(object sender, ChildIndexChangedEventArgs e) private void ChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)
{ {
// Run matching again if: // Run matching again if:
// 1. Selector is reversed, so other item insertion/deletion might affect total count without changing subscribed item index. // 1. Selector is reversed, so other item insertion/deletion might affect total count without changing subscribed item index.

2
src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs

@ -66,7 +66,7 @@ namespace Avalonia.Styling.Activators
_classes.CollectionChanged -= ClassesChangedHandler; _classes.CollectionChanged -= ClassesChangedHandler;
} }
private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) private void ClassesChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.Action != NotifyCollectionChangedAction.Move) if (e.Action != NotifyCollectionChangedAction.Move)
{ {

6
src/Avalonia.Styling/Styling/ChildSelector.cs

@ -6,7 +6,7 @@ namespace Avalonia.Styling
internal class ChildSelector : Selector internal class ChildSelector : Selector
{ {
private readonly Selector _parent; private readonly Selector _parent;
private string _selectorString; private string? _selectorString;
public ChildSelector(Selector parent) public ChildSelector(Selector parent)
{ {
@ -25,7 +25,7 @@ namespace Avalonia.Styling
public override bool IsCombinator => true; public override bool IsCombinator => true;
/// <inheritdoc/> /// <inheritdoc/>
public override Type TargetType => null; public override Type? TargetType => null;
public override string ToString() public override string ToString()
{ {
@ -64,6 +64,6 @@ namespace Avalonia.Styling
} }
} }
protected override Selector MovePrevious() => null; protected override Selector? MovePrevious() => null;
} }
} }

2
src/Avalonia.Styling/Styling/OrSelector.cs

@ -120,7 +120,7 @@ namespace Avalonia.Styling
} }
else else
{ {
while (!result.IsAssignableFrom(selector.TargetType)) while (result is not null && !result.IsAssignableFrom(selector.TargetType))
{ {
result = result.BaseType; result = result.BaseType;
} }

7
src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs

@ -53,11 +53,16 @@ namespace Avalonia.Styling
if (_property.IsAttached) if (_property.IsAttached)
{ {
builder.Append('(');
builder.Append(_property.OwnerType.Name); builder.Append(_property.OwnerType.Name);
builder.Append('.'); builder.Append('.');
} }
builder.Append(_property.Name); builder.Append(_property.Name);
if (_property.IsAttached)
{
builder.Append(')');
}
builder.Append('='); builder.Append('=');
builder.Append(_value ?? string.Empty); builder.Append(_value ?? string.Empty);
builder.Append(']'); builder.Append(']');
@ -104,7 +109,7 @@ namespace Avalonia.Styling
var converter = TypeDescriptor.GetConverter(propertyType); var converter = TypeDescriptor.GetConverter(propertyType);
if (converter?.CanConvertFrom(valueType) == true) if (converter?.CanConvertFrom(valueType) == true)
{ {
return Equals(propertyValue, converter.ConvertFrom(null, CultureInfo.InvariantCulture, value)); return Equals(propertyValue, converter.ConvertFrom(null, CultureInfo.InvariantCulture, value!));
} }
return false; return false;

50
src/Avalonia.Styling/Styling/Selectors.cs

@ -25,10 +25,14 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="name">The name of the style class.</param> /// <param name="name">The name of the style class.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Class(this Selector previous, string name) public static Selector Class(this Selector? previous, string name)
{ {
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(name));
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name may not be empty", nameof(name));
}
var tac = previous as TypeNameAndClassSelector; var tac = previous as TypeNameAndClassSelector;
@ -48,7 +52,7 @@ namespace Avalonia.Styling
/// </summary> /// </summary>
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Descendant(this Selector previous) public static Selector Descendant(this Selector? previous)
{ {
return new DescendantSelector(previous); return new DescendantSelector(previous);
} }
@ -59,9 +63,9 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Is(this Selector previous, Type type) public static Selector Is(this Selector? previous, Type type)
{ {
Contract.Requires<ArgumentNullException>(type != null); _ = type ?? throw new ArgumentNullException(nameof(type));
return TypeNameAndClassSelector.Is(previous, type); return TypeNameAndClassSelector.Is(previous, type);
} }
@ -72,7 +76,7 @@ namespace Avalonia.Styling
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type.</typeparam>
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Is<T>(this Selector previous) where T : IStyleable public static Selector Is<T>(this Selector? previous) where T : IStyleable
{ {
return previous.Is(typeof(T)); return previous.Is(typeof(T));
} }
@ -83,10 +87,14 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Name(this Selector previous, string name) public static Selector Name(this Selector? previous, string name)
{ {
Contract.Requires<ArgumentNullException>(name != null); _ = name ?? throw new ArgumentNullException(nameof(name));
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(name));
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name may not be empty", nameof(name));
}
var tac = previous as TypeNameAndClassSelector; var tac = previous as TypeNameAndClassSelector;
@ -107,7 +115,7 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="argument">The selector to be not-ed.</param> /// <param name="argument">The selector to be not-ed.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Not(this Selector previous, Func<Selector, Selector> argument) public static Selector Not(this Selector? previous, Func<Selector?, Selector> argument)
{ {
return new NotSelector(previous, argument(null)); return new NotSelector(previous, argument(null));
} }
@ -118,7 +126,7 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="argument">The selector to be not-ed.</param> /// <param name="argument">The selector to be not-ed.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector Not(this Selector previous, Selector argument) public static Selector Not(this Selector? previous, Selector argument)
{ {
return new NotSelector(previous, argument); return new NotSelector(previous, argument);
} }
@ -126,7 +134,7 @@ namespace Avalonia.Styling
/// <inheritdoc cref="NthChildSelector"/> /// <inheritdoc cref="NthChildSelector"/>
/// <inheritdoc cref="NthChildSelector(Selector?, int, int)"/> /// <inheritdoc cref="NthChildSelector(Selector?, int, int)"/>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector NthChild(this Selector previous, int step, int offset) public static Selector NthChild(this Selector? previous, int step, int offset)
{ {
return new NthChildSelector(previous, step, offset); return new NthChildSelector(previous, step, offset);
} }
@ -134,7 +142,7 @@ namespace Avalonia.Styling
/// <inheritdoc cref="NthLastChildSelector"/> /// <inheritdoc cref="NthLastChildSelector"/>
/// <inheritdoc cref="NthLastChildSelector(Selector?, int, int)"/> /// <inheritdoc cref="NthLastChildSelector(Selector?, int, int)"/>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector NthLastChild(this Selector previous, int step, int offset) public static Selector NthLastChild(this Selector? previous, int step, int offset)
{ {
return new NthLastChildSelector(previous, step, offset); return new NthLastChildSelector(previous, step, offset);
} }
@ -145,9 +153,9 @@ namespace Avalonia.Styling
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector OfType(this Selector previous, Type type) public static Selector OfType(this Selector? previous, Type type)
{ {
Contract.Requires<ArgumentNullException>(type != null); _ = type ?? throw new ArgumentNullException(nameof(type));
return TypeNameAndClassSelector.OfType(previous, type); return TypeNameAndClassSelector.OfType(previous, type);
} }
@ -158,7 +166,7 @@ namespace Avalonia.Styling
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type.</typeparam>
/// <param name="previous">The previous selector.</param> /// <param name="previous">The previous selector.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector OfType<T>(this Selector previous) where T : IStyleable public static Selector OfType<T>(this Selector? previous) where T : IStyleable
{ {
return previous.OfType(typeof(T)); return previous.OfType(typeof(T));
} }
@ -191,9 +199,9 @@ namespace Avalonia.Styling
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="value">The property value.</param> /// <param name="value">The property value.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector PropertyEquals<T>(this Selector previous, AvaloniaProperty<T> property, object value) public static Selector PropertyEquals<T>(this Selector? previous, AvaloniaProperty<T> property, object? value)
{ {
Contract.Requires<ArgumentNullException>(property != null); _ = property ?? throw new ArgumentNullException(nameof(property));
return new PropertyEqualsSelector(previous, property, value); return new PropertyEqualsSelector(previous, property, value);
} }
@ -205,9 +213,9 @@ namespace Avalonia.Styling
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="value">The property value.</param> /// <param name="value">The property value.</param>
/// <returns>The selector.</returns> /// <returns>The selector.</returns>
public static Selector PropertyEquals(this Selector previous, AvaloniaProperty property, object value) public static Selector PropertyEquals(this Selector? previous, AvaloniaProperty property, object? value)
{ {
Contract.Requires<ArgumentNullException>(property != null); _ = property ?? throw new ArgumentNullException(nameof(property));
return new PropertyEqualsSelector(previous, property, value); return new PropertyEqualsSelector(previous, property, value);
} }

14
src/Avalonia.Styling/Styling/Styles.cs

@ -262,7 +262,7 @@ namespace Avalonia.Styling
} }
} }
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
static IReadOnlyList<T> ToReadOnlyList<T>(IList list) static IReadOnlyList<T> ToReadOnlyList<T>(IList list)
{ {
@ -282,7 +282,7 @@ namespace Avalonia.Styling
{ {
for (var i = 0; i < items.Count; ++i) for (var i = 0; i < items.Count; ++i)
{ {
var style = (IStyle)items[i]; var style = (IStyle)items[i]!;
if (Owner is object && style is IResourceProvider resourceProvider) if (Owner is object && style is IResourceProvider resourceProvider)
{ {
@ -299,7 +299,7 @@ namespace Avalonia.Styling
{ {
for (var i = 0; i < items.Count; ++i) for (var i = 0; i < items.Count; ++i)
{ {
var style = (IStyle)items[i]; var style = (IStyle)items[i]!;
if (Owner is object && style is IResourceProvider resourceProvider) if (Owner is object && style is IResourceProvider resourceProvider)
{ {
@ -315,14 +315,14 @@ namespace Avalonia.Styling
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Add(e.NewItems); Add(e.NewItems!);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Remove(e.OldItems); Remove(e.OldItems!);
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
Remove(e.OldItems); Remove(e.OldItems!);
Add(e.NewItems); Add(e.NewItems!);
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
throw new InvalidOperationException("Reset should not be called on Styles."); throw new InvalidOperationException("Reset should not be called on Styles.");

6
src/Avalonia.Styling/Styling/TemplateSelector.cs

@ -5,7 +5,7 @@ namespace Avalonia.Styling
internal class TemplateSelector : Selector internal class TemplateSelector : Selector
{ {
private readonly Selector _parent; private readonly Selector _parent;
private string _selectorString; private string? _selectorString;
public TemplateSelector(Selector parent) public TemplateSelector(Selector parent)
{ {
@ -24,7 +24,7 @@ namespace Avalonia.Styling
public override bool IsCombinator => true; public override bool IsCombinator => true;
/// <inheritdoc/> /// <inheritdoc/>
public override Type TargetType => null; public override Type? TargetType => null;
public override string ToString() public override string ToString()
{ {
@ -48,6 +48,6 @@ namespace Avalonia.Styling
return _parent.Match(templatedParent, subscribe); return _parent.Match(templatedParent, subscribe);
} }
protected override Selector MovePrevious() => null; protected override Selector? MovePrevious() => null;
} }
} }

2
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

6
src/Avalonia.Themes.Default/CalendarDatePicker.xaml

@ -115,7 +115,11 @@
StaysOpen="False"> StaysOpen="False">
<Calendar Name="PART_Calendar" <Calendar Name="PART_Calendar"
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}" FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"/> IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"
SelectedDate="{TemplateBinding SelectedDate, Mode=TwoWay}"
DisplayDate="{TemplateBinding DisplayDate}"
DisplayDateStart="{TemplateBinding DisplayDateStart}"
DisplayDateEnd="{TemplateBinding DisplayDateEnd}" />
</Popup> </Popup>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>

2
src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

6
src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml

@ -129,7 +129,11 @@
IsLightDismissEnabled="True"> IsLightDismissEnabled="True">
<Calendar Name="PART_Calendar" <Calendar Name="PART_Calendar"
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}" FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"/> IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"
SelectedDate="{TemplateBinding SelectedDate, Mode=TwoWay}"
DisplayDate="{TemplateBinding DisplayDate}"
DisplayDateStart="{TemplateBinding DisplayDateStart}"
DisplayDateEnd="{TemplateBinding DisplayDateEnd}" />
</Popup> </Popup>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>

2
src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs

@ -143,7 +143,7 @@ namespace Avalonia.Animation.Animators
if (!match(firstKeyType)) if (!match(firstKeyType))
continue; continue;
animator = (IAnimator)Activator.CreateInstance(animatorType); animator = (IAnimator?)Activator.CreateInstance(animatorType);
if (animator != null) if (animator != null)
{ {
animator.Property = Property; animator.Property = Property;

4
src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs

@ -11,10 +11,10 @@ namespace Avalonia.Animation.Animators
/// </summary> /// </summary>
public class TransformAnimator : Animator<double> public class TransformAnimator : Animator<double>
{ {
DoubleAnimator _doubleAnimator; DoubleAnimator? _doubleAnimator;
/// <inheritdoc/> /// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete) public override IDisposable? Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> obsMatch, Action onComplete)
{ {
var ctrl = (Visual)control; var ctrl = (Visual)control;

2
src/Avalonia.Visuals/Animation/CompositePageTransition.cs

@ -37,7 +37,7 @@ namespace Avalonia.Animation
public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>(); public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>();
/// <inheritdoc /> /// <inheritdoc />
public Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
var transitionTasks = PageTransitions var transitionTasks = PageTransitions
.Select(transition => transition.Start(from, to, forward, cancellationToken)) .Select(transition => transition.Start(from, to, forward, cancellationToken))

6
src/Avalonia.Visuals/Animation/CrossFade.cs

@ -100,7 +100,7 @@ namespace Avalonia.Animation
} }
/// <inheritdoc cref="Start(Visual, Visual, CancellationToken)" /> /// <inheritdoc cref="Start(Visual, Visual, CancellationToken)" />
public async Task Start(Visual from, Visual to, CancellationToken cancellationToken) public async Task Start(Visual? from, Visual? to, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -112,7 +112,7 @@ namespace Avalonia.Animation
{ {
if (to != null) if (to != null)
{ {
disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)); disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)!);
} }
if (from != null) if (from != null)
@ -151,7 +151,7 @@ namespace Avalonia.Animation
/// <returns> /// <returns>
/// A <see cref="Task"/> that tracks the progress of the animation. /// A <see cref="Task"/> that tracks the progress of the animation.
/// </returns> /// </returns>
Task IPageTransition.Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) Task IPageTransition.Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
return Start(from, to, cancellationToken); return Start(from, to, cancellationToken);
} }

2
src/Avalonia.Visuals/Animation/IPageTransition.cs

@ -26,6 +26,6 @@ namespace Avalonia.Animation
/// <returns> /// <returns>
/// A <see cref="Task"/> that tracks the progress of the animation. /// A <see cref="Task"/> that tracks the progress of the animation.
/// </returns> /// </returns>
Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken); Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken);
} }
} }

10
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -62,7 +62,7 @@ namespace Avalonia.Animation
public Easing SlideOutEasing { get; set; } = new LinearEasing(); public Easing SlideOutEasing { get; set; } = new LinearEasing();
/// <inheritdoc /> /// <inheritdoc />
public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@ -155,17 +155,17 @@ namespace Avalonia.Animation
/// <remarks> /// <remarks>
/// Any one of the parameters may be null, but not both. /// Any one of the parameters may be null, but not both.
/// </remarks> /// </remarks>
private static IVisual GetVisualParent(IVisual from, IVisual to) private static IVisual GetVisualParent(IVisual? from, IVisual? to)
{ {
var p1 = (from ?? to).VisualParent; var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from).VisualParent; var p2 = (to ?? from)!.VisualParent;
if (p1 != null && p2 != null && p1 != p2) if (p1 != null && p2 != null && p1 != p2)
{ {
throw new ArgumentException("Controls for PageSlide must have same parent."); throw new ArgumentException("Controls for PageSlide must have same parent.");
} }
return p1; return p1 ?? throw new InvalidOperationException("Cannot determine visual parent.");
} }
} }
} }

2
src/Avalonia.Visuals/Animation/RenderLoopClock.cs

@ -9,7 +9,7 @@ namespace Avalonia.Animation
{ {
protected override void Stop() protected override void Stop()
{ {
AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this); AvaloniaLocator.Current.GetRequiredService<IRenderLoop>().Remove(this);
} }
bool IRenderLoopTask.NeedsUpdate => HasSubscriptions; bool IRenderLoopTask.NeedsUpdate => HasSubscriptions;

4
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -5,6 +5,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract.
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
@ -77,4 +79,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
Total Issues: 78 Total Issues: 79

3
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
@ -15,4 +15,5 @@
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\System.Memory.props" /> <Import Project="..\..\build\System.Memory.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project> </Project>

2
src/Avalonia.Visuals/CornerRadius.cs

@ -91,7 +91,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="obj">The Object to compare against.</param> /// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this corner radius; False otherwise.</returns> /// <returns>True if the Object is equal to this corner radius; False otherwise.</returns>
public override bool Equals(object obj) => obj is CornerRadius other && Equals(other); public override bool Equals(object? obj) => obj is CornerRadius other && Equals(other);
public override int GetHashCode() public override int GetHashCode()
{ {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save