committed by
GitHub
158 changed files with 2915 additions and 2180 deletions
@ -1,17 +1,18 @@ |
|||
<Project> |
|||
<Import Project="$(MSBuildThisFileDirectory)/build/AvaloniaPublicKey.props"/> |
|||
<Import Project="$(MSBuildThisFileDirectory)/build/TargetFrameworks.props"/> |
|||
<Import Project="$(MSBuildThisFileDirectory)/build/WarnAsErrors.props" /> |
|||
<PropertyGroup> |
|||
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath> |
|||
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\$(AvsCurrentTargetFramework)\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> |
|||
<!-- https://github.com/dotnet/msbuild/issues/2661 --> |
|||
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies> |
|||
<RunApiCompat>False</RunApiCompat> |
|||
<LangVersion>14.0</LangVersion> |
|||
<CreateHardLinksForCopyAdditionalFilesIfPossible>true</CreateHardLinksForCopyAdditionalFilesIfPossible> |
|||
<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible> |
|||
<CreateHardLinksForCopyLocalIfPossible>true</CreateHardLinksForCopyLocalIfPossible> |
|||
<CreateHardLinksForPublishFilesIfPossible>true</CreateHardLinksForPublishFilesIfPossible> |
|||
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath> |
|||
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\$(AvsCurrentTargetFramework)\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> |
|||
<!-- https://github.com/dotnet/msbuild/issues/2661 --> |
|||
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies> |
|||
<RunApiCompat>False</RunApiCompat> |
|||
<LangVersion>14.0</LangVersion> |
|||
<TreatWarningsAsErrors Condition="'$(TreatWarningsAsErrors)' == ''">$(AvnTreatWarningsAsErrors)</TreatWarningsAsErrors> |
|||
<TreatWarningsAsErrors Condition="'$(TreatWarningsAsErrors)' == '' And '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors> |
|||
<CreateHardLinksForCopyAdditionalFilesIfPossible>true</CreateHardLinksForCopyAdditionalFilesIfPossible> |
|||
<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible> |
|||
<CreateHardLinksForCopyLocalIfPossible>true</CreateHardLinksForCopyLocalIfPossible> |
|||
<CreateHardLinksForPublishFilesIfPossible>true</CreateHardLinksForPublishFilesIfPossible> |
|||
</PropertyGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,76 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids --> |
|||
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|||
<Suppression> |
|||
<DiagnosticId>CP0001</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0001</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaUIFactDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0001</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0001</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaUIFactDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0002</DiagnosticId> |
|||
<Target>M:Avalonia.Headless.XUnit.AvaloniaFactAttribute.#ctor</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0002</DiagnosticId> |
|||
<Target>M:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer.#ctor(Xunit.Abstractions.IMessageSink)</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0002</DiagnosticId> |
|||
<Target>M:Avalonia.Headless.XUnit.AvaloniaFactAttribute.#ctor</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0002</DiagnosticId> |
|||
<Target>M:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer.#ctor(Xunit.Abstractions.IMessageSink)</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0007</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0007</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0008</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkAttribute</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
<Suppression> |
|||
<DiagnosticId>CP0008</DiagnosticId> |
|||
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkAttribute</Target> |
|||
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
|||
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
|||
</Suppression> |
|||
</Suppressions> |
|||
File diff suppressed because it is too large
@ -1,11 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<!-- |
|||
The Nullable annotations on netstandard2.0 are incomplete and incorrect in places. Ignore |
|||
nullable warnings before .NET 6 and make them errors on later target frameworks. |
|||
--> |
|||
<PropertyGroup> |
|||
<Nullable>enable</Nullable> |
|||
<WarningsAsErrors Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">$(WarningsAsErrors);nullable</WarningsAsErrors> |
|||
<NoWarn Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">$(NoWarn);nullable</NoWarn> |
|||
<!-- The nullable annotations on netstandard2.0 are incomplete and incorrect in places. Ignore them. --> |
|||
<NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);nullable</NoWarn> |
|||
</PropertyGroup> |
|||
</Project> |
|||
|
|||
@ -1,28 +1,16 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
|
|||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))"> |
|||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))"> |
|||
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings> |
|||
<EnableTrimAnalyzer>true</EnableTrimAnalyzer> |
|||
<TrimmerSingleWarn>false</TrimmerSingleWarn> |
|||
<IsTrimmable>true</IsTrimmable> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))"> |
|||
<IsAotCompatible Condition="'$(IsAotCompatible)' == ''">true</IsAotCompatible> |
|||
<ILLinkTreatWarningsAsErrors>$(TreatWarningsAsErrors)</ILLinkTreatWarningsAsErrors> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))"> |
|||
<ILLinkTreatWarningsAsErrors>true</ILLinkTreatWarningsAsErrors> |
|||
<!-- Trim warnings --> |
|||
<WarningsAsErrors>$(WarningsAsErrors);IL2000;IL2001;IL2002;IL2003;IL2004;IL2005;IL2006;IL2007;IL2008;IL2009;IL2010;IL2011;IL2012;IL2013;IL2014;IL2015;IL2016;IL2017;IL2018;IL2019;IL2020;IL2021;IL2022;IL2023;IL2024;IL2025;IL2026;IL2027;IL2028;IL2029;IL2030;IL2031;IL2032;IL2033;IL2034;IL2035;IL2036;IL2037;IL2038;IL2039;IL2040;IL2041;IL2042;IL2043;IL2044;IL2045;IL2046;IL2047;IL2048;IL2049;IL2050;IL2051;IL2052;IL2053;IL2054;IL2055;IL2056;IL2057;IL2058;IL2059;IL2060;IL2061;IL2062;IL2063;IL2064;IL2065;IL2066;IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091;IL2092;IL2093;IL2094;IL2095;IL2096;IL2097;IL2098;IL2099;IL2100;IL2101;IL2102;IL2103;IL2104;IL2105;IL2106;IL2107;IL2108;IL2109;IL2110;IL2111;IL2112;IL2113;IL2114;IL2115;IL2116;IL2117;IL2118;IL2119;IL2120;IL2121;IL2122;IL2123;IL2124;IL2125;IL2126;IL2127;IL2128;IL2129;IL2130;IL2131;IL2132;IL2133;IL2134;IL2135;IL2136;IL2137;IL2138;IL2139;IL2140;IL2141;IL2142;IL2143;IL2144;IL2145;IL2146;IL2147;IL2148;IL2149;IL2150;IL2151;IL2152;IL2153;IL2154;IL2155;IL2156;IL2157</WarningsAsErrors> |
|||
<!-- NativeAOT warnings --> |
|||
<WarningsAsErrors Condition="'$(IsAotCompatible)' == 'true'">$(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056</WarningsAsErrors> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0')) AND '$(EnableRuntimeMarshalling)' != 'true'"> |
|||
<WarningsAsErrors>$(WarningsAsErrors);CA1420;CA1421</WarningsAsErrors> |
|||
</PropertyGroup> |
|||
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0')) AND '$(EnableRuntimeMarshalling)' != 'true'"> |
|||
<AssemblyAttribute Include="System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
|
|||
@ -1,23 +0,0 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<!-- Keep in sync with .editorconfig --> |
|||
<PropertyGroup Condition="'$(AvsEnableDevWarningsNotAsErrors)' == 'true'"> |
|||
<!-- CS0649: Field 'field' is never assigned to, and will always have its default value 'value' --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0649</WarningsNotAsErrors> |
|||
<!-- CS0162: Remove unreachable code --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0162</WarningsNotAsErrors> |
|||
<!-- CA2211:Non-constant fields should not be visible --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CA2211</WarningsNotAsErrors> |
|||
<!-- CA1821: Remove empty finalizers --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CA1821</WarningsNotAsErrors> |
|||
<!-- CA1823: Avoid unused private fields --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CA1823</WarningsNotAsErrors> |
|||
<!-- AVLN2203: DuplicateSetterError --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);AVLN2203</WarningsNotAsErrors> |
|||
<!-- AVLN2205: RequiredTemplatePartMissing --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);AVLN2205</WarningsNotAsErrors> |
|||
<!-- AVLN2207: TemplatePartWrongType --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);AVLN2207</WarningsNotAsErrors> |
|||
<!-- AVLN2208: ItemContainerInsideTemplate --> |
|||
<WarningsNotAsErrors>$(WarningsNotAsErrors);AVLN2208</WarningsNotAsErrors> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -1,14 +1,13 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup Condition="'$(IsXUnit2)'!='true'"> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.1" /> |
|||
</ItemGroup> |
|||
<ItemGroup Condition="'$(IsXUnit2)'=='true'"> |
|||
<PackageReference Include="xunit" Version="2.9.3" /> |
|||
<PackageReference Include="YTest.MTP.XUnit2" Version="1.0.2" /> |
|||
</ItemGroup> |
|||
|
|||
<PropertyGroup> |
|||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile> |
|||
<SignAssembly>False</SignAssembly> |
|||
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- ignore signing warnings for unit tests --> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
|||
|
|||
@ -1 +1 @@ |
|||
Subproject commit 9738c6121fdd143c78d3e25686a7e4e13c00f586 |
|||
Subproject commit 5530e1cbe9e105ff4ebc9da1f4af3253a8756754 |
|||
@ -1,11 +0,0 @@ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace Avalonia.Controls.Primitives; |
|||
|
|||
// TODO12: Integrate with existing IScrollable interface, breaking change
|
|||
internal interface IInternalScroller |
|||
{ |
|||
bool CanHorizontallyScroll { get; } |
|||
|
|||
bool CanVerticallyScroll { get; } |
|||
} |
|||
@ -0,0 +1,164 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data.Converters; |
|||
using Avalonia.Data.Core; |
|||
using Avalonia.Data.Core.ExpressionNodes; |
|||
using Avalonia.Data.Core.Parsers; |
|||
|
|||
namespace Avalonia.Data; |
|||
|
|||
/// <summary>
|
|||
/// A binding which does not use reflection to access members.
|
|||
/// </summary>
|
|||
public class CompiledBinding : BindingBase |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CompiledBinding"/> class.
|
|||
/// </summary>
|
|||
public CompiledBinding() { } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CompiledBinding"/> class.
|
|||
/// </summary>
|
|||
/// <param name="path">The binding path.</param>
|
|||
public CompiledBinding(CompiledBindingPath path) => Path = path; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the amount of time, in milliseconds, to wait before updating the binding
|
|||
/// source after the value on the target changes.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// There is no delay when the source is updated via <see cref="UpdateSourceTrigger.LostFocus"/>
|
|||
/// or <see cref="BindingExpressionBase.UpdateSource"/>. Nor is there a delay when
|
|||
/// <see cref="BindingMode.OneWayToSource"/> is active and a new source object is provided.
|
|||
/// </remarks>
|
|||
public int Delay { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="IValueConverter"/> to use.
|
|||
/// </summary>
|
|||
public IValueConverter? Converter { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the culture in which to evaluate the converter.
|
|||
/// </summary>
|
|||
/// <value>The default value is null.</value>
|
|||
/// <remarks>
|
|||
/// If this property is not set then <see cref="CultureInfo.CurrentCulture"/> will be used.
|
|||
/// </remarks>
|
|||
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))] |
|||
public CultureInfo? ConverterCulture { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
|
|||
/// </summary>
|
|||
public object? ConverterParameter { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the value to use when the binding is unable to produce a value.
|
|||
/// </summary>
|
|||
public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding mode.
|
|||
/// </summary>
|
|||
public BindingMode Mode { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding path.
|
|||
/// </summary>
|
|||
public CompiledBindingPath? Path { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding priority.
|
|||
/// </summary>
|
|||
public BindingPriority Priority { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the source for the binding.
|
|||
/// </summary>
|
|||
public object? Source { get; set; } = AvaloniaProperty.UnsetValue; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the string format.
|
|||
/// </summary>
|
|||
public string? StringFormat { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the value to use when the binding result is null.
|
|||
/// </summary>
|
|||
public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value that determines the timing of binding source updates for
|
|||
/// <see cref="BindingMode.TwoWay"/> and <see cref="BindingMode.OneWayToSource"/> bindings.
|
|||
/// </summary>
|
|||
public UpdateSourceTrigger UpdateSourceTrigger { get; set; } |
|||
|
|||
internal WeakReference? DefaultAnchor { get; set; } |
|||
internal WeakReference<INameScope?>? NameScope { get; set; } |
|||
|
|||
internal override BindingExpressionBase CreateInstance( |
|||
AvaloniaObject target, |
|||
AvaloniaProperty? targetProperty, |
|||
object? anchor) |
|||
{ |
|||
var enableDataValidation = targetProperty?.GetMetadata(target).EnableDataValidation ?? false; |
|||
var nodes = new List<ExpressionNode>(); |
|||
var isRooted = false; |
|||
|
|||
Path?.BuildExpression(nodes, out isRooted); |
|||
|
|||
// If the binding isn't rooted (i.e. doesn't have a Source or start with $parent, $self,
|
|||
// #elementName etc.) then we need to add a data context source node.
|
|||
if (Source == AvaloniaProperty.UnsetValue && !isRooted) |
|||
nodes.Insert(0, ExpressionNodeFactory.CreateDataContext(targetProperty)); |
|||
|
|||
// If the first node is an ISourceNode then allow it to select the source; otherwise
|
|||
// use the binding source if specified, falling back to the target.
|
|||
var source = nodes?.Count > 0 && nodes[0] is SourceNode sn |
|||
? sn.SelectSource(Source, target, anchor ?? DefaultAnchor?.Target) |
|||
: Source != AvaloniaProperty.UnsetValue ? Source : target; |
|||
|
|||
var (mode, trigger) = ResolveDefaultsFromMetadata(target, targetProperty); |
|||
|
|||
return new BindingExpression( |
|||
source, |
|||
nodes, |
|||
FallbackValue, |
|||
delay: TimeSpan.FromMilliseconds(Delay), |
|||
converter: Converter, |
|||
converterCulture: ConverterCulture, |
|||
converterParameter: ConverterParameter, |
|||
enableDataValidation: enableDataValidation, |
|||
mode: mode, |
|||
priority: Priority, |
|||
stringFormat: StringFormat, |
|||
targetNullValue: TargetNullValue, |
|||
targetProperty: targetProperty, |
|||
targetTypeConverter: TargetTypeConverter.GetDefaultConverter(), |
|||
updateSourceTrigger: trigger); |
|||
} |
|||
|
|||
private (BindingMode, UpdateSourceTrigger) ResolveDefaultsFromMetadata( |
|||
AvaloniaObject target, |
|||
AvaloniaProperty? targetProperty) |
|||
{ |
|||
var mode = Mode; |
|||
var trigger = UpdateSourceTrigger == UpdateSourceTrigger.Default ? |
|||
UpdateSourceTrigger.PropertyChanged : UpdateSourceTrigger; |
|||
|
|||
if (mode == BindingMode.Default) |
|||
{ |
|||
if (targetProperty?.GetMetadata(target) is { } metadata) |
|||
mode = metadata.DefaultBindingMode; |
|||
else |
|||
mode = BindingMode.OneWay; |
|||
} |
|||
|
|||
return (mode, trigger); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Specifies the baseline pixel alignment options for rendering text or graphics.
|
|||
/// </summary>
|
|||
/// <remarks>Use this enumeration to control whether the baseline of rendered content is aligned to the
|
|||
/// pixel grid, which can affect visual crispness and positioning. The value may influence rendering quality,
|
|||
/// especially at small font sizes or when precise alignment is required.</remarks>
|
|||
public enum BaselinePixelAlignment : byte |
|||
{ |
|||
/// <summary>
|
|||
/// The baseline pixel alignment is unspecified.
|
|||
/// </summary>
|
|||
Unspecified, |
|||
|
|||
/// <summary>
|
|||
/// The baseline is aligned to the pixel grid.
|
|||
/// </summary>
|
|||
Aligned, |
|||
|
|||
/// <summary>
|
|||
/// The baseline is not aligned to the pixel grid.
|
|||
/// </summary>
|
|||
Unaligned |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Specifies the level of hinting applied to text glyphs during rendering.
|
|||
/// Text hinting adjusts glyph outlines to improve readability and crispness,
|
|||
/// especially at small font sizes or low DPI. This enum controls the amount
|
|||
/// of grid-fitting and outline adjustment performed.
|
|||
/// </summary>
|
|||
public enum TextHintingMode : byte |
|||
{ |
|||
/// <summary>
|
|||
/// Hinting mode is not explicitly specified. The default will be used.
|
|||
/// </summary>
|
|||
Unspecified, |
|||
|
|||
/// <summary>
|
|||
/// No hinting, outlines are scaled only.
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// Minimal hinting, preserves glyph shape.
|
|||
/// </summary>
|
|||
Light, |
|||
|
|||
/// <summary>
|
|||
/// Aggressive grid-fitting, maximum crispness at low DPI.
|
|||
/// </summary>
|
|||
Strong |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Provides options for controlling text rendering behavior, including rendering mode, hinting mode, and baseline
|
|||
/// pixel alignment. Used to configure how text appears within visual elements.
|
|||
/// </summary>
|
|||
/// <remarks>TextOptions encapsulates settings that influence the clarity, sharpness, and positioning of
|
|||
/// rendered text. These options can be applied to visual elements to customize text appearance for different
|
|||
/// display scenarios, such as optimizing for readability at small font sizes or ensuring pixel-perfect alignment.
|
|||
/// The struct supports merging with other instances to inherit unspecified values, and exposes attached properties
|
|||
/// for use with visuals.</remarks>
|
|||
public readonly record struct TextOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the text rendering mode used to control how text glyphs are rendered.
|
|||
/// </summary>
|
|||
public TextRenderingMode TextRenderingMode { get; init; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the text rendering hinting mode used to optimize the display of text.
|
|||
/// </summary>
|
|||
/// <remarks>The hinting mode determines how text is rendered to improve clarity and readability,
|
|||
/// especially at small font sizes. Changing this value may affect the appearance of text depending on the
|
|||
/// rendering engine and display device.</remarks>
|
|||
public TextHintingMode TextHintingMode { get; init; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the text baseline should be aligned to the pixel grid.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When enabled, the vertical position of the text baseline is snapped to whole pixel boundaries.
|
|||
/// This ensures consistent sharpness and reduces blurriness caused by fractional positioning,
|
|||
/// particularly at small font sizes or low DPI settings.
|
|||
/// </remarks>
|
|||
public BaselinePixelAlignment BaselinePixelAlignment { get; init; } |
|||
|
|||
/// <summary>
|
|||
/// Merges this instance with <paramref name="other"/> using inheritance semantics: unspecified values on this
|
|||
/// instance are taken from <paramref name="other"/>.
|
|||
/// </summary>
|
|||
public TextOptions MergeWith(TextOptions other) |
|||
{ |
|||
var textRenderingMode = TextRenderingMode; |
|||
|
|||
if (textRenderingMode == TextRenderingMode.Unspecified) |
|||
{ |
|||
textRenderingMode = other.TextRenderingMode; |
|||
} |
|||
|
|||
var textHintingMode = TextHintingMode; |
|||
|
|||
if (textHintingMode == TextHintingMode.Unspecified) |
|||
{ |
|||
textHintingMode = other.TextHintingMode; |
|||
} |
|||
|
|||
var baselinePixelAlignment = BaselinePixelAlignment; |
|||
|
|||
if (baselinePixelAlignment == BaselinePixelAlignment.Unspecified) |
|||
{ |
|||
baselinePixelAlignment = other.BaselinePixelAlignment; |
|||
} |
|||
|
|||
return new TextOptions |
|||
{ |
|||
TextRenderingMode = textRenderingMode, |
|||
TextHintingMode = textHintingMode, |
|||
BaselinePixelAlignment = baselinePixelAlignment |
|||
}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the TextOptions attached value for a visual.
|
|||
/// </summary>
|
|||
public static TextOptions GetTextOptions(Visual visual) |
|||
{ |
|||
return visual.TextOptions; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the TextOptions attached value for a visual.
|
|||
/// </summary>
|
|||
public static void SetTextOptions(Visual visual, TextOptions value) |
|||
{ |
|||
visual.TextOptions = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the TextRenderingMode attached property for a visual.
|
|||
/// </summary>
|
|||
public static TextRenderingMode GetTextRenderingMode(Visual visual) |
|||
{ |
|||
return visual.TextOptions.TextRenderingMode; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the TextRenderingMode attached property for a visual.
|
|||
/// </summary>
|
|||
public static void SetTextRenderingMode(Visual visual, TextRenderingMode value) |
|||
{ |
|||
visual.TextOptions = visual.TextOptions with { TextRenderingMode = value }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the TextHintingMode attached property for a visual.
|
|||
/// </summary>
|
|||
public static TextHintingMode GetTextHintingMode(Visual visual) |
|||
{ |
|||
return visual.TextOptions.TextHintingMode; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the TextHintingMode attached property for a visual.
|
|||
/// </summary>
|
|||
public static void SetTextHintingMode(Visual visual, TextHintingMode value) |
|||
{ |
|||
visual.TextOptions = visual.TextOptions with { TextHintingMode = value }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the BaselinePixelAlignment attached property for a visual.
|
|||
/// </summary>
|
|||
public static BaselinePixelAlignment GetBaselinePixelAlignment(Visual visual) |
|||
{ |
|||
return visual.TextOptions.BaselinePixelAlignment; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the BaselinePixelAlignment attached property for a visual.
|
|||
/// </summary>
|
|||
public static void SetBaselinePixelAlignment(Visual visual, BaselinePixelAlignment value) |
|||
{ |
|||
visual.TextOptions = visual.TextOptions with { BaselinePixelAlignment = value }; |
|||
} |
|||
} |
|||
} |
|||
@ -1,11 +1,36 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Specifies how text glyphs are rendered in Avalonia.
|
|||
/// Controls the smoothing and antialiasing applied during text rasterization.
|
|||
/// </summary>
|
|||
public enum TextRenderingMode : byte |
|||
{ |
|||
/// <summary>
|
|||
/// Rendering mode is not explicitly specified.
|
|||
/// The system or platform default will be used.
|
|||
/// </summary>
|
|||
Unspecified, |
|||
|
|||
SubpixelAntialias, |
|||
/// <summary>
|
|||
/// Glyphs are rendered with subpixel antialiasing.
|
|||
/// This provides higher apparent resolution on LCD screens
|
|||
/// by using the individual red, green, and blue subpixels.
|
|||
/// </summary>
|
|||
SubpixelAntialias, |
|||
|
|||
/// <summary>
|
|||
/// Glyphs are rendered with standard grayscale antialiasing.
|
|||
/// This smooths edges without using subpixel information,
|
|||
/// preserving shape fidelity across different display types.
|
|||
/// </summary>
|
|||
Antialias, |
|||
|
|||
/// <summary>
|
|||
/// Glyphs are rendered without antialiasing.
|
|||
/// This produces sharp, aliased edges and may be useful
|
|||
/// for pixel-art aesthetics or low-DPI environments.
|
|||
/// </summary>
|
|||
Alias |
|||
} |
|||
} |
|||
|
|||
@ -1,46 +0,0 @@ |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
// TODO12 combine with IGeometryContext
|
|||
public interface IGeometryContext2 : IGeometryContext |
|||
{ |
|||
/// <summary>
|
|||
/// Draws a line to the specified point.
|
|||
/// </summary>
|
|||
/// <param name="point">The destination point.</param>
|
|||
/// <param name="isStroked">Whether the segment is stroked</param>
|
|||
void LineTo(Point point, bool isStroked); |
|||
|
|||
/// <summary>
|
|||
/// Draws an arc to the specified point.
|
|||
/// </summary>
|
|||
/// <param name="point">The destination point.</param>
|
|||
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
|
|||
/// <param name="rotationAngle">The rotation angle (in radians) of the oval that specifies the curve.</param>
|
|||
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
|
|||
/// <param name="sweepDirection">
|
|||
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
|
|||
/// </param>
|
|||
/// <param name="isStroked">Whether the segment is stroked</param>
|
|||
void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked); |
|||
|
|||
/// <summary>
|
|||
/// Draws a Bezier curve to the specified point.
|
|||
/// </summary>
|
|||
/// <param name="controlPoint1">The first control point used to specify the shape of the curve.</param>
|
|||
/// <param name="controlPoint2">The second control point used to specify the shape of the curve.</param>
|
|||
/// <param name="endPoint">The destination point for the end of the curve.</param>
|
|||
/// <param name="isStroked">Whether the segment is stroked</param>
|
|||
void CubicBezierTo(Point controlPoint1, Point controlPoint2, Point endPoint, bool isStroked); |
|||
|
|||
/// <summary>
|
|||
/// Draws a quadratic Bezier curve to the specified point
|
|||
/// </summary>
|
|||
/// <param name="controlPoint ">Control point</param>
|
|||
/// <param name="endPoint">DestinationPoint</param>
|
|||
/// <param name="isStroked">Whether the segment is stroked</param>
|
|||
void QuadraticBezierTo(Point controlPoint, Point endPoint, bool isStroked); |
|||
} |
|||
|
|||
} |
|||
@ -1,245 +0,0 @@ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.Globalization; |
|||
using static System.Char; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
// TODO12: Remove this struct in 12.0 (breaking change)
|
|||
|
|||
[Obsolete("This type has been superseded by SpanStringTokenizer.")] |
|||
#if !BUILDTASK
|
|||
public |
|||
#endif
|
|||
record struct StringTokenizer : IDisposable |
|||
{ |
|||
private const char DefaultSeparatorChar = ','; |
|||
|
|||
private readonly string _s; |
|||
private readonly int _length; |
|||
private readonly char _separator; |
|||
private readonly string? _exceptionMessage; |
|||
private readonly IFormatProvider _formatProvider; |
|||
private int _index; |
|||
private int _tokenIndex; |
|||
private int _tokenLength; |
|||
|
|||
public StringTokenizer(string s, IFormatProvider formatProvider, string? exceptionMessage = null) |
|||
: this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage) |
|||
{ |
|||
_formatProvider = formatProvider; |
|||
} |
|||
|
|||
public StringTokenizer(string s, char separator = DefaultSeparatorChar, string? exceptionMessage = null) |
|||
{ |
|||
_s = s ?? throw new ArgumentNullException(nameof(s)); |
|||
_length = s?.Length ?? 0; |
|||
_separator = separator; |
|||
_exceptionMessage = exceptionMessage; |
|||
_formatProvider = CultureInfo.InvariantCulture; |
|||
_index = 0; |
|||
_tokenIndex = -1; |
|||
_tokenLength = 0; |
|||
|
|||
while (_index < _length && IsWhiteSpace(_s, _index)) |
|||
{ |
|||
_index++; |
|||
} |
|||
} |
|||
|
|||
public string? CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); |
|||
|
|||
public ReadOnlySpan<char> CurrentTokenSpan => _tokenIndex < 0 ? ReadOnlySpan<char>.Empty : _s.AsSpan().Slice(_tokenIndex, _tokenLength); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (_index != _length) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
|
|||
public bool TryReadInt32(out Int32 result, char? separator = null) |
|||
{ |
|||
if (TryReadSpan(out var stringResult, separator) && |
|||
SpanHelpers.TryParseInt(stringResult, NumberStyles.Integer, _formatProvider, out result)) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
result = default; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public int ReadInt32(char? separator = null) |
|||
{ |
|||
if (!TryReadInt32(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool TryReadDouble(out double result, char? separator = null) |
|||
{ |
|||
if (TryReadSpan(out var stringResult, separator) && |
|||
SpanHelpers.TryParseDouble(stringResult, NumberStyles.Float, _formatProvider, out result)) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
result = default; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public double ReadDouble(char? separator = null) |
|||
{ |
|||
if (!TryReadDouble(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool TryReadString([NotNull] out string result, char? separator = null) |
|||
{ |
|||
var success = TryReadToken(separator ?? _separator); |
|||
result = CurrentTokenSpan.ToString(); |
|||
return success; |
|||
} |
|||
|
|||
public string ReadString(char? separator = null) |
|||
{ |
|||
if (!TryReadString(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool TryReadSpan(out ReadOnlySpan<char> result, char? separator = null) |
|||
{ |
|||
var success = TryReadToken(separator ?? _separator); |
|||
result = CurrentTokenSpan; |
|||
return success; |
|||
} |
|||
|
|||
public ReadOnlySpan<char> ReadSpan(char? separator = null) |
|||
{ |
|||
if (!TryReadSpan(out var result, separator)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private bool TryReadToken(char separator) |
|||
{ |
|||
_tokenIndex = -1; |
|||
|
|||
if (_index >= _length) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var c = _s[_index]; |
|||
|
|||
var index = _index; |
|||
var length = 0; |
|||
|
|||
while (_index < _length) |
|||
{ |
|||
c = _s[_index]; |
|||
|
|||
if (IsWhiteSpace(c) || c == separator) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
_index++; |
|||
length++; |
|||
} |
|||
|
|||
SkipToNextToken(separator); |
|||
|
|||
_tokenIndex = index; |
|||
_tokenLength = length; |
|||
|
|||
if (_tokenLength < 1) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private void SkipToNextToken(char separator) |
|||
{ |
|||
if (_index < _length) |
|||
{ |
|||
var c = _s[_index]; |
|||
|
|||
if (c != separator && !IsWhiteSpace(c)) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
|
|||
var length = 0; |
|||
|
|||
while (_index < _length) |
|||
{ |
|||
c = _s[_index]; |
|||
|
|||
if (c == separator) |
|||
{ |
|||
length++; |
|||
_index++; |
|||
|
|||
if (length > 1) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (!IsWhiteSpace(c)) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
_index++; |
|||
} |
|||
} |
|||
|
|||
if (length > 0 && _index >= _length) |
|||
{ |
|||
throw GetFormatException(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private FormatException GetFormatException() => |
|||
_exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException(); |
|||
|
|||
private static char GetSeparatorFromFormatProvider(IFormatProvider provider) |
|||
{ |
|||
var c = DefaultSeparatorChar; |
|||
|
|||
var formatInfo = NumberFormatInfo.GetInstance(provider); |
|||
if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0]) |
|||
{ |
|||
c = ';'; |
|||
} |
|||
|
|||
return c; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using Avalonia.Automation; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Automation.Provider; |
|||
|
|||
namespace Avalonia.Controls.Automation.Peers |
|||
{ |
|||
public class ExpanderAutomationPeer : ControlAutomationPeer, |
|||
IExpandCollapseProvider |
|||
{ |
|||
public ExpanderAutomationPeer(Control owner) |
|||
: base(owner) |
|||
{ |
|||
owner.PropertyChanged += OwnerPropertyChanged; |
|||
} |
|||
|
|||
public new Expander Owner => (Expander)base.Owner; |
|||
|
|||
public ExpandCollapseState ExpandCollapseState => ToState(Owner.IsExpanded); |
|||
public bool ShowsMenu => false; |
|||
public void Collapse() => Owner.IsExpanded = false; |
|||
public void Expand() => Owner.IsExpanded = true; |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.Expander; |
|||
} |
|||
|
|||
private void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property == Expander.IsExpandedProperty) |
|||
{ |
|||
RaisePropertyChangedEvent( |
|||
ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, |
|||
ToState((bool)e.OldValue!), |
|||
ToState((bool)e.NewValue!)); |
|||
} |
|||
} |
|||
|
|||
private static ExpandCollapseState ToState(bool value) |
|||
{ |
|||
return value ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => true; |
|||
protected override bool IsControlElementCore() => true; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using Avalonia.Automation.Provider; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Contains values used as identifiers by <see cref="IToggleProvider"/>.
|
|||
/// </summary>
|
|||
public static class TogglePatternIdentifiers |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies the <see cref="IToggleProvider.ToggleState"/> property.
|
|||
/// </summary>
|
|||
public static AutomationProperty ToggleStateProperty { get; } = new AutomationProperty(); |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Threading; |
|||
using Xunit.Sdk; |
|||
using Xunit.v3; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal sealed class AvaloniaDelayEnumeratedTheoryTestCase |
|||
: XunitDelayEnumeratedTheoryTestCase, ISelfExecutingXunitTestCase |
|||
{ |
|||
public AvaloniaDelayEnumeratedTheoryTestCase( |
|||
IXunitTestMethod testMethod, |
|||
string testCaseDisplayName, |
|||
string uniqueID, |
|||
bool @explicit, |
|||
bool skipTestWithoutData, |
|||
Type[]? skipExceptions = null, |
|||
string? skipReason = null, |
|||
Type? skipType = null, |
|||
string? skipUnless = null, |
|||
string? skipWhen = null, |
|||
Dictionary<string, HashSet<string>>? traits = null, |
|||
string? sourceFilePath = null, |
|||
int? sourceLineNumber = null, |
|||
int? timeout = null) |
|||
: base( |
|||
testMethod, |
|||
testCaseDisplayName, |
|||
uniqueID, |
|||
@explicit, |
|||
skipTestWithoutData, |
|||
skipExceptions, |
|||
skipReason, |
|||
skipType, |
|||
skipUnless, |
|||
skipWhen, |
|||
traits, |
|||
sourceFilePath, |
|||
sourceLineNumber, |
|||
timeout) |
|||
{ |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] |
|||
public AvaloniaDelayEnumeratedTheoryTestCase() |
|||
{ |
|||
} |
|||
|
|||
public async ValueTask<RunSummary> Run( |
|||
ExplicitOption explicitOption, |
|||
IMessageBus messageBus, |
|||
object?[] constructorArguments, |
|||
ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource) |
|||
{ |
|||
var tests = await aggregator.RunAsync(CreateTests, []); |
|||
|
|||
// We need to block the XUnit thread to ensure its concurrency throttle is effective.
|
|||
// See https://github.com/AArnott/Xunit.StaFact/pull/55#issuecomment-826187354 for details.
|
|||
var runSummary = Task.Run(async () => await AvaloniaTestCaseRunner.Instance.Run( |
|||
this, |
|||
tests, |
|||
messageBus, |
|||
aggregator, |
|||
cancellationTokenSource, |
|||
TestCaseDisplayName, |
|||
SkipReason, |
|||
explicitOption, |
|||
constructorArguments)) |
|||
.GetAwaiter() |
|||
.GetResult(); |
|||
|
|||
return runSummary; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using Xunit.Internal; |
|||
using Xunit.Sdk; |
|||
using Xunit.v3; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public class AvaloniaFactDiscoverer : FactDiscoverer |
|||
{ |
|||
protected override IXunitTestCase CreateTestCase( |
|||
ITestFrameworkDiscoveryOptions discoveryOptions, |
|||
IXunitTestMethod testMethod, |
|||
IFactAttribute factAttribute) |
|||
{ |
|||
var details = TestIntrospectionHelper.GetTestCaseDetails(discoveryOptions, testMethod, factAttribute); |
|||
|
|||
return new AvaloniaTestCase( |
|||
details.ResolvedTestMethod, |
|||
details.TestCaseDisplayName, |
|||
details.UniqueID, |
|||
details.Explicit, |
|||
details.SkipExceptions, |
|||
details.SkipReason, |
|||
details.SkipType, |
|||
details.SkipUnless, |
|||
details.SkipWhen, |
|||
testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), |
|||
sourceFilePath: details.SourceFilePath, |
|||
sourceLineNumber: details.SourceLineNumber, |
|||
timeout: details.Timeout); |
|||
} |
|||
} |
|||
@ -1,126 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Threading; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal class AvaloniaTestAssemblyRunner : XunitTestAssemblyRunner |
|||
{ |
|||
private HeadlessUnitTestSession? _session; |
|||
|
|||
public AvaloniaTestAssemblyRunner(ITestAssembly testAssembly, IEnumerable<IXunitTestCase> testCases, |
|||
IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, |
|||
ITestFrameworkExecutionOptions executionOptions) : base(testAssembly, testCases, diagnosticMessageSink, |
|||
executionMessageSink, executionOptions) |
|||
{ |
|||
} |
|||
|
|||
protected override void SetupSyncContext(int maxParallelThreads) |
|||
{ |
|||
_session = HeadlessUnitTestSession.GetOrStartForAssembly( |
|||
Assembly.Load(new AssemblyName(TestAssembly.Assembly.Name))); |
|||
base.SetupSyncContext(1); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
_session?.Dispose(); |
|||
base.Dispose(); |
|||
} |
|||
|
|||
protected override Task<RunSummary> RunTestCollectionAsync( |
|||
IMessageBus messageBus, |
|||
ITestCollection testCollection, |
|||
IEnumerable<IXunitTestCase> testCases, |
|||
CancellationTokenSource cancellationTokenSource) |
|||
{ |
|||
return new AvaloniaTestCollectionRunner(_session!, testCollection, testCases, DiagnosticMessageSink, messageBus, |
|||
TestCaseOrderer, new ExceptionAggregator(Aggregator), cancellationTokenSource).RunAsync(); |
|||
} |
|||
|
|||
private class AvaloniaTestCollectionRunner : XunitTestCollectionRunner |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
|
|||
public AvaloniaTestCollectionRunner(HeadlessUnitTestSession session, |
|||
ITestCollection testCollection, IEnumerable<IXunitTestCase> testCases, |
|||
IMessageSink diagnosticMessageSink, IMessageBus messageBus, ITestCaseOrderer testCaseOrderer, |
|||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(testCollection, |
|||
testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource) |
|||
{ |
|||
_session = session; |
|||
} |
|||
|
|||
protected override Task<RunSummary> RunTestClassAsync( |
|||
ITestClass testClass, |
|||
IReflectionTypeInfo @class, |
|||
IEnumerable<IXunitTestCase> testCases) |
|||
{ |
|||
return new AvaloniaTestClassRunner(_session, testClass, @class, testCases, DiagnosticMessageSink, MessageBus, |
|||
TestCaseOrderer, new ExceptionAggregator(Aggregator), CancellationTokenSource, |
|||
CollectionFixtureMappings).RunAsync(); |
|||
} |
|||
} |
|||
|
|||
private class AvaloniaTestClassRunner : XunitTestClassRunner |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
|
|||
public AvaloniaTestClassRunner(HeadlessUnitTestSession session, ITestClass testClass, |
|||
IReflectionTypeInfo @class, |
|||
IEnumerable<IXunitTestCase> testCases, IMessageSink diagnosticMessageSink, IMessageBus messageBus, |
|||
ITestCaseOrderer testCaseOrderer, ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource, IDictionary<Type, object> collectionFixtureMappings) : |
|||
base(testClass, @class, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, |
|||
cancellationTokenSource, collectionFixtureMappings) |
|||
{ |
|||
_session = session; |
|||
} |
|||
|
|||
protected override Task<RunSummary> RunTestMethodAsync( |
|||
ITestMethod testMethod, |
|||
IReflectionMethodInfo method, |
|||
IEnumerable<IXunitTestCase> testCases, |
|||
object[] constructorArguments) |
|||
{ |
|||
return new AvaloniaTestMethodRunner(_session, testMethod, Class, method, testCases, DiagnosticMessageSink, |
|||
MessageBus, new ExceptionAggregator(Aggregator), CancellationTokenSource, |
|||
constructorArguments).RunAsync(); |
|||
} |
|||
} |
|||
|
|||
private class AvaloniaTestMethodRunner : XunitTestMethodRunner |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
private readonly IMessageBus _messageBus; |
|||
private readonly ExceptionAggregator _aggregator; |
|||
private readonly CancellationTokenSource _cancellationTokenSource; |
|||
private readonly object[] _constructorArguments; |
|||
|
|||
public AvaloniaTestMethodRunner(HeadlessUnitTestSession session, ITestMethod testMethod, |
|||
IReflectionTypeInfo @class, |
|||
IReflectionMethodInfo method, IEnumerable<IXunitTestCase> testCases, IMessageSink diagnosticMessageSink, |
|||
IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource, |
|||
object[] constructorArguments) : base(testMethod, @class, method, testCases, diagnosticMessageSink, |
|||
messageBus, aggregator, cancellationTokenSource, constructorArguments) |
|||
{ |
|||
_session = session; |
|||
_messageBus = messageBus; |
|||
_aggregator = aggregator; |
|||
_cancellationTokenSource = cancellationTokenSource; |
|||
_constructorArguments = constructorArguments; |
|||
} |
|||
|
|||
protected override Task<RunSummary> RunTestCaseAsync(IXunitTestCase testCase) |
|||
{ |
|||
return AvaloniaTestCaseRunner.RunTest(_session, testCase, testCase.DisplayName, testCase.SkipReason, |
|||
_constructorArguments, testCase.TestMethodArguments, _messageBus, _aggregator, |
|||
_cancellationTokenSource); |
|||
} |
|||
} |
|||
} |
|||
@ -1,103 +1,59 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Threading; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
using Xunit.v3; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal class AvaloniaTestCaseRunner : XunitTestCaseRunner |
|||
internal sealed class AvaloniaTestCaseRunner |
|||
: XunitTestCaseRunnerBase<AvaloniaTestCaseRunnerContext, IXunitTestCase, IXunitTest> |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
private readonly Action? _onAfterTestInvoked; |
|||
public static AvaloniaTestCaseRunner Instance { get; } = new(); |
|||
|
|||
public AvaloniaTestCaseRunner( |
|||
HeadlessUnitTestSession session, Action? onAfterTestInvoked, |
|||
IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, |
|||
object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource) : base(testCase, displayName, skipReason, constructorArguments, |
|||
testMethodArguments, messageBus, aggregator, cancellationTokenSource) |
|||
private AvaloniaTestCaseRunner() |
|||
{ |
|||
_session = session; |
|||
_onAfterTestInvoked = onAfterTestInvoked; |
|||
} |
|||
|
|||
public static Task<RunSummary> RunTest(HeadlessUnitTestSession session, |
|||
IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, |
|||
object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource) |
|||
public async ValueTask<RunSummary> Run( |
|||
IXunitTestCase testCase, |
|||
IReadOnlyCollection<IXunitTest> tests, |
|||
IMessageBus messageBus, |
|||
ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource, |
|||
string displayName, |
|||
string? skipReason, |
|||
ExplicitOption explicitOption, |
|||
object?[] constructorArguments) |
|||
{ |
|||
var afterTest = () => Dispatcher.UIThread.RunJobs(); |
|||
|
|||
var runner = new AvaloniaTestCaseRunner(session, afterTest, testCase, displayName, |
|||
skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource); |
|||
return runner.RunAsync(); |
|||
} |
|||
|
|||
protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, |
|||
object[] constructorArguments, |
|||
MethodInfo testMethod, object[] testMethodArguments, string skipReason, |
|||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
|||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) |
|||
{ |
|||
return new AvaloniaTestRunner(_session, _onAfterTestInvoked, test, messageBus, testClass, constructorArguments, |
|||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); |
|||
} |
|||
|
|||
private class AvaloniaTestRunner : XunitTestRunner |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
private readonly Action? _onAfterTestInvoked; |
|||
|
|||
public AvaloniaTestRunner( |
|||
HeadlessUnitTestSession session, Action? onAfterTestInvoked, |
|||
ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, |
|||
object[] testMethodArguments, string skipReason, |
|||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource) : base(test, messageBus, testClass, constructorArguments, |
|||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource) |
|||
{ |
|||
_session = session; |
|||
_onAfterTestInvoked = onAfterTestInvoked; |
|||
} |
|||
|
|||
protected override Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator) |
|||
{ |
|||
return _session.Dispatch( |
|||
() => new AvaloniaTestInvoker(_onAfterTestInvoked, Test, MessageBus, TestClass, |
|||
ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, |
|||
CancellationTokenSource).RunAsync(), |
|||
CancellationTokenSource.Token); |
|||
} |
|||
var session = HeadlessUnitTestSession.GetOrStartForAssembly(testCase.TestClass.Class.Assembly); |
|||
|
|||
await using var ctxt = new AvaloniaTestCaseRunnerContext( |
|||
testCase, |
|||
tests, |
|||
messageBus, |
|||
aggregator, |
|||
cancellationTokenSource, |
|||
displayName, |
|||
skipReason, |
|||
explicitOption, |
|||
constructorArguments, |
|||
session); |
|||
await ctxt.InitializeAsync(); |
|||
|
|||
return await Run(ctxt); |
|||
} |
|||
|
|||
private class AvaloniaTestInvoker : XunitTestInvoker |
|||
{ |
|||
private readonly Action? _onAfterTestInvoked; |
|||
|
|||
public AvaloniaTestInvoker( |
|||
Action? onAfterTestInvoked, |
|||
ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, |
|||
object[] testMethodArguments, IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
|||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(test, messageBus, |
|||
testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, |
|||
cancellationTokenSource) |
|||
{ |
|||
_onAfterTestInvoked = onAfterTestInvoked; |
|||
} |
|||
|
|||
protected override async Task AfterTestMethodInvokedAsync() |
|||
{ |
|||
await base.AfterTestMethodInvokedAsync(); |
|||
|
|||
// Only here we can execute random code after the test, where exception will be properly handled by the XUnit.
|
|||
if (_onAfterTestInvoked is not null) |
|||
{ |
|||
Aggregator.Run(_onAfterTestInvoked); |
|||
} |
|||
} |
|||
} |
|||
protected override ValueTask<RunSummary> RunTest( |
|||
AvaloniaTestCaseRunnerContext ctxt, |
|||
IXunitTest test) |
|||
=> AvaloniaTestRunner.Instance.Run( |
|||
test, |
|||
ctxt.MessageBus, |
|||
ctxt.ConstructorArguments, |
|||
ctxt.ExplicitOption, |
|||
ctxt.Aggregator.Clone(), |
|||
ctxt.CancellationTokenSource, |
|||
ctxt.BeforeAfterTestAttributes, |
|||
ctxt.Session); |
|||
} |
|||
|
|||
@ -0,0 +1,31 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using Xunit.Sdk; |
|||
using Xunit.v3; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal sealed class AvaloniaTestCaseRunnerContext( |
|||
IXunitTestCase testCase, |
|||
IReadOnlyCollection<IXunitTest> tests, |
|||
IMessageBus messageBus, |
|||
ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource, |
|||
string displayName, |
|||
string? skipReason, |
|||
ExplicitOption explicitOption, |
|||
object?[] constructorArguments, |
|||
HeadlessUnitTestSession session) |
|||
: XunitTestCaseRunnerContext( |
|||
testCase, |
|||
tests, |
|||
messageBus, |
|||
aggregator, |
|||
cancellationTokenSource, |
|||
displayName, |
|||
skipReason, |
|||
explicitOption, |
|||
constructorArguments) |
|||
{ |
|||
public HeadlessUnitTestSession Session { get; } = session; |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue