diff --git a/.editorconfig b/.editorconfig
index 3f51b29e89..c3989db7f4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -139,12 +139,16 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CA1802: Use literals where appropriate
dotnet_diagnostic.CA1802.severity = warning
+# CA1820: Test for empty strings using string length
+dotnet_diagnostic.CA1820.severity = warning
# CA1821: Remove empty finalizers
dotnet_diagnostic.CA1821.severity = warning
# CA1822: Mark members as static
dotnet_diagnostic.CA1822.severity = warning
# CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = warning
+#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
+dotnet_diagnostic.CA1847.severity = warning
# Wrapping preferences
csharp_wrap_before_ternary_opsigns = false
diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml
index ee8abb75c1..4fba4ca36f 100644
--- a/azure-pipelines-integrationtests.yml
+++ b/azure-pipelines-integrationtests.yml
@@ -12,6 +12,16 @@ jobs:
name: 'AvaloniaMacPool'
steps:
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 6.0.401'
+ inputs:
+ version: 6.0.401
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
+ inputs:
+ version: 7.0.100-rc.2.22477.23
+
- script: system_profiler SPDisplaysDataType |grep Resolution
- script: |
@@ -45,6 +55,11 @@ jobs:
inputs:
version: 6.0.401
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
+ inputs:
+ version: 7.0.100-rc.2.22477.23
+
- task: Windows Application Driver@0
inputs:
OperationType: 'Start'
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 33b2dc670a..903f9e3843 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -35,9 +35,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
@@ -72,9 +72,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
@@ -143,9 +143,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
diff --git a/global.json b/global.json
index 44d4e10dbf..dc6da556b3 100644
--- a/global.json
+++ b/global.json
@@ -1,4 +1,8 @@
{
+ "sdk": {
+ "version": "7.0.100-rc.2.22477.23",
+ "rollForward": "latestFeature"
+ },
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
"MSBuild.Sdk.Extras": "3.0.22",
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index d43a5c1624..4b9e33ffe8 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -3,7 +3,6 @@
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false
low
- <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false
<_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false
@@ -71,7 +70,6 @@
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
- EmbeddedResources="@(EmbeddedResources)"
ReportImportance="$(AvaloniaXamlReportImportance)"/>
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 0667644643..6c17e9ac43 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -5,6 +5,7 @@
net6.0
true
true
+ 6.0.9
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
index ad54eb95fc..69ceaea328 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
@@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
-
-
@@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
-
-
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
index 6e017e381f..4671bbdb7c 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
@@ -1,6 +1,8 @@
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Layout;
using Avalonia.Markup.Xaml;
+using Avalonia.Media;
namespace ControlCatalog.Pages
{
@@ -9,6 +11,20 @@ namespace ControlCatalog.Pages
public ColorPickerPage()
{
InitializeComponent();
+
+ var layoutRoot = this.GetControl("LayoutRoot");
+
+ // ColorPicker added from code-behind
+ var colorPicker = new ColorPicker()
+ {
+ Color = Colors.Blue,
+ Margin = new Thickness(0, 50, 0, 0),
+ HorizontalAlignment = HorizontalAlignment.Center,
+ };
+ Grid.SetColumn(colorPicker, 2);
+ Grid.SetRow(colorPicker, 1);
+
+ layoutRoot.Children.Add(colorPicker);
}
private void InitializeComponent()
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index fd43ced196..46ba4082fb 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -41,7 +41,7 @@ namespace Avalonia
{
_ = name ?? throw new ArgumentNullException(nameof(name));
- if (name.Contains("."))
+ if (name.Contains('.'))
{
throw new ArgumentException("'name' may not contain periods.");
}
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index d86e723b38..62265d3c59 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -228,7 +228,7 @@ namespace Avalonia
_ = type ?? throw new ArgumentNullException(nameof(type));
_ = name ?? throw new ArgumentNullException(nameof(name));
- if (name.Contains("."))
+ if (name.Contains('.'))
{
throw new InvalidOperationException("Attached properties not supported.");
}
diff --git a/src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs b/src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs
new file mode 100644
index 0000000000..45e41b44d6
--- /dev/null
+++ b/src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs
@@ -0,0 +1,12 @@
+using System.Runtime.CompilerServices;
+
+namespace System;
+
+#if !NET6_0_OR_GREATER
+public static class StringCompatibilityExtensions
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Contains(this string str, char search) =>
+ str.Contains(search.ToString());
+}
+#endif
diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs
index cb90404f6d..5470a735b3 100644
--- a/src/Avalonia.Base/Media/Color.cs
+++ b/src/Avalonia.Base/Media/Color.cs
@@ -9,6 +9,7 @@ using System;
using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators;
+using static Avalonia.Utilities.SpanHelpers;
#endif
namespace Avalonia.Media
@@ -295,9 +296,7 @@ namespace Avalonia.Media
return false;
}
- // TODO: (netstandard 2.1) Can use allocation free parsing.
- if (!uint.TryParse(input.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out var parsed))
+ if (!input.TryParseUInt(NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsed))
{
return false;
}
@@ -382,9 +381,9 @@ namespace Avalonia.Media
if (components.Length == 3) // RGB
{
- if (InternalTryParseByte(components[0], out byte red) &&
- InternalTryParseByte(components[1], out byte green) &&
- InternalTryParseByte(components[2], out byte blue))
+ if (InternalTryParseByte(components[0].AsSpan(), out byte red) &&
+ InternalTryParseByte(components[1].AsSpan(), out byte green) &&
+ InternalTryParseByte(components[2].AsSpan(), out byte blue))
{
color = new Color(0xFF, red, green, blue);
return true;
@@ -392,10 +391,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // RGBA
{
- if (InternalTryParseByte(components[0], out byte red) &&
- InternalTryParseByte(components[1], out byte green) &&
- InternalTryParseByte(components[2], out byte blue) &&
- InternalTryParseDouble(components[3], out double alpha))
+ if (InternalTryParseByte(components[0].AsSpan(), out byte red) &&
+ InternalTryParseByte(components[1].AsSpan(), out byte green) &&
+ InternalTryParseByte(components[2].AsSpan(), out byte blue) &&
+ InternalTryParseDouble(components[3].AsSpan(), out double alpha))
{
color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue);
return true;
@@ -403,17 +402,14 @@ namespace Avalonia.Media
}
// Local function to specially parse a byte value with an optional percentage sign
- bool InternalTryParseByte(string inString, out byte outByte)
+ bool InternalTryParseByte(ReadOnlySpan inString, out byte outByte)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out double percentage);
outByte = (byte)Math.Round((percentage / 100.0) * 255.0);
@@ -421,37 +417,28 @@ namespace Avalonia.Media
}
else
{
- return byte.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseByte(NumberStyles.Number, CultureInfo.InvariantCulture,
out outByte);
}
}
// Local function to specially parse a double value with an optional percentage sign
- bool InternalTryParseDouble(string inString, out double outDouble)
+ bool InternalTryParseDouble(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs
index 485bb1db16..425a3138c3 100644
--- a/src/Avalonia.Base/Media/HslColor.cs
+++ b/src/Avalonia.Base/Media/HslColor.cs
@@ -302,9 +302,9 @@ namespace Avalonia.Media
if (components.Length == 3) // HSL
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double lightness))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double lightness))
{
hslColor = new HslColor(1.0, hue, saturation, lightness);
return true;
@@ -312,10 +312,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // HSLA
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double lightness) &&
- TryInternalParse(components[3], out double alpha))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double lightness) &&
+ TryInternalParse(components[3].AsSpan(), out double alpha))
{
hslColor = new HslColor(alpha, hue, saturation, lightness);
return true;
@@ -323,28 +323,22 @@ namespace Avalonia.Media
}
// Local function to specially parse a double value with an optional percentage sign
- bool TryInternalParse(string inString, out double outDouble)
+ bool TryInternalParse(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs
index 512e57ae07..9f95b31518 100644
--- a/src/Avalonia.Base/Media/HsvColor.cs
+++ b/src/Avalonia.Base/Media/HsvColor.cs
@@ -302,9 +302,9 @@ namespace Avalonia.Media
if (components.Length == 3) // HSV
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double value))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double value))
{
hsvColor = new HsvColor(1.0, hue, saturation, value);
return true;
@@ -312,10 +312,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // HSVA
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double value) &&
- TryInternalParse(components[3], out double alpha))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double value) &&
+ TryInternalParse(components[3].AsSpan(), out double alpha))
{
hsvColor = new HsvColor(alpha, hue, saturation, value);
return true;
@@ -323,28 +323,22 @@ namespace Avalonia.Media
}
// Local function to specially parse a double value with an optional percentage sign
- bool TryInternalParse(string inString, out double outDouble)
+ bool TryInternalParse(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs
index e2338b9b26..344b85bae9 100644
--- a/src/Avalonia.Base/Media/UnicodeRange.cs
+++ b/src/Avalonia.Base/Media/UnicodeRange.cs
@@ -163,7 +163,7 @@ namespace Avalonia.Media
throw new FormatException("Could not parse specified Unicode range segment.");
}
- if (!single.Value.Contains("?"))
+ if (!single.Value.Contains('?'))
{
start = int.Parse(single.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
end = start;
diff --git a/src/Avalonia.Base/Utilities/IdentifierParser.cs b/src/Avalonia.Base/Utilities/IdentifierParser.cs
index b105d0746b..ee176a6b85 100644
--- a/src/Avalonia.Base/Utilities/IdentifierParser.cs
+++ b/src/Avalonia.Base/Utilities/IdentifierParser.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Utilities
#endif
static class IdentifierParser
{
- public static ReadOnlySpan ParseIdentifier(this ref CharacterReader r)
+ public static ReadOnlySpan ParseIdentifier(this scoped ref CharacterReader r)
{
if (IsValidIdentifierStart(r.Peek))
{
diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs
new file mode 100644
index 0000000000..9a5dce9798
--- /dev/null
+++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
+namespace Avalonia.Utilities
+{
+ public static class SpanHelpers
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseUInt(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out uint value)
+ {
+#if NETSTANDARD2_0
+ return uint.TryParse(span.ToString(), style, provider, out value);
+#else
+ return uint.TryParse(span, style, provider, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseInt(this ReadOnlySpan span, out int value)
+ {
+#if NETSTANDARD2_0
+ return int.TryParse(span.ToString(), out value);
+#else
+ return int.TryParse(span, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseDouble(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out double value)
+ {
+#if NETSTANDARD2_0
+ return double.TryParse(span.ToString(), style, provider, out value);
+#else
+ return double.TryParse(span, style, provider, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseByte(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out byte value)
+ {
+#if NETSTANDARD2_0
+ return byte.TryParse(span.ToString(), style, provider, out value);
+#else
+ return byte.TryParse(span, style, provider, out value);
+#endif
+ }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 62a9ed27be..13e8b14891 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
@@ -37,8 +37,7 @@ namespace Avalonia.Build.Tasks
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath, VerifyIl, outputImportance,
- (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
- EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch);
+ (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, SkipXamlCompilation, DebuggerLaunch);
if (!res.Success)
return false;
if (!res.WrittenFile)
@@ -72,8 +71,6 @@ namespace Avalonia.Build.Tasks
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
-
- public bool EnableComInteropPatching { get; set; }
public bool SkipXamlCompilation { get; set; }
public string AssemblyOriginatorKeyFile { get; set; }
diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
index 75758d1315..38450ea2a2 100644
--- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
+++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
@@ -18,8 +18,6 @@ namespace Avalonia.Build.Tasks
public string Root { get; set; }
[Required]
public string Output { get; set; }
- [Required]
- public ITaskItem[] EmbeddedResources { get; set; }
public string ReportImportance { get; set; }
@@ -148,10 +146,6 @@ namespace Avalonia.Build.Tasks
Enum.TryParse(ReportImportance, out _reportImportance);
BuildEngine.LogMessage($"GenerateAvaloniaResourcesTask -> Root: {Root}, {Resources?.Count()} resources, Output:{Output}", _reportImportance < MessageImportance.Low ? MessageImportance.High : _reportImportance);
-
- foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml") || r.ItemSpec.EndsWith(".axaml")))
- BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec,
- "XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
var resources = BuildResourceSources();
if (!PreProcessXamlFiles(resources))
diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs
index e3f00968fd..f42fab5964 100644
--- a/src/Avalonia.Build.Tasks/Program.cs
+++ b/src/Avalonia.Build.Tasks/Program.cs
@@ -44,8 +44,7 @@ namespace Avalonia.Build.Tasks
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory(),
- VerifyIl = true,
- EnableComInteropPatching = true
+ VerifyIl = true
}.Execute() ?
0 :
2;
diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs
index 25f8d0175a..be59ff8b6c 100644
--- a/src/Avalonia.Build.Tasks/SpanCompat.cs
+++ b/src/Avalonia.Build.Tasks/SpanCompat.cs
@@ -1,4 +1,7 @@
#if !NETCOREAPP3_1_OR_GREATER
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
namespace System
{
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@@ -9,6 +12,8 @@ namespace System
private int _length;
public int Length => _length;
+ public static implicit operator ReadOnlySpan(string s) => new ReadOnlySpan(s);
+
public ReadOnlySpan(string s) : this(s, 0, s.Length)
{
@@ -63,8 +68,75 @@ namespace System
return Slice(start);
}
+ public ReadOnlySpan TrimEnd()
+ {
+ int end = Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (!char.IsWhiteSpace(this[end]))
+ {
+ break;
+ }
+ }
+ return Slice(0, end + 1);
+ }
+
+ public ReadOnlySpan Trim()
+ {
+ return TrimStart().TrimEnd();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryParseUInt(NumberStyles style, IFormatProvider provider, out uint value)
+ {
+ return uint.TryParse(ToString(), style, provider, out value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryParseInt(out int value)
+ {
+ return int.TryParse(ToString(), out value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryParseDouble(NumberStyles style, IFormatProvider provider, out double value)
+ {
+ return double.TryParse(ToString(), style, provider, out value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryParseByte(NumberStyles style, IFormatProvider provider, out byte value)
+ {
+ return byte.TryParse(ToString(), style, provider, out value);
+ }
+
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
+ internal int IndexOf(ReadOnlySpan v, StringComparison ordinal, int start = 0)
+ {
+ if(Length == 0 || v.IsEmpty)
+ {
+ return -1;
+ }
+
+ for (var c = start; c < _length; c++)
+ {
+ if (this[c] == v[0])
+ {
+ for(var i = 0; i < v.Length; i++)
+ {
+ if (this[c + i] != v[i])
+ {
+ break;
+ }
+ }
+ return c;
+ }
+ }
+
+ return -1;
+ }
+
public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr));
}
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
index 5a2c74e16f..f83e07bd74 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
@@ -24,38 +24,6 @@ namespace Avalonia.Build.Tasks
string Name { get; }
IEnumerable Resources { get; }
}
-
- class EmbeddedResources : IResourceGroup
- {
- private readonly AssemblyDefinition _asm;
- public string Name => "EmbeddedResource";
-
- public IEnumerable Resources => _asm.MainModule.Resources.OfType()
- .Select(r => new WrappedResource(_asm, r)).ToList();
-
- public EmbeddedResources(AssemblyDefinition asm)
- {
- _asm = asm;
- }
- class WrappedResource : IResource
- {
- private readonly AssemblyDefinition _asm;
- private readonly EmbeddedResource _res;
-
- public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
- {
- _asm = asm;
- _res = res;
- }
-
- public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
- public string Name => _res.Name;
- public string FilePath => Name;
- public byte[] FileContents => _res.GetResourceData();
-
- public void Remove() => _asm.MainModule.Resources.Remove(_res);
- }
- }
class AvaloniaResources : IResourceGroup
{
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 5915388822..d2ccea8fa4 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -39,15 +39,15 @@ namespace Avalonia.Build.Tasks
public static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
- string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom,
+ string output, bool verifyIl, MessageImportance logImportance, string strongNameKey,
bool skipXamlCompilation)
{
- return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false);
+ return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
}
internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
- string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
+ string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
{
var typeSystem = new CecilTypeSystem(
references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
@@ -58,15 +58,12 @@ namespace Avalonia.Build.Tasks
if (!skipXamlCompilation)
{
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch);
- if (compileRes == null && !patchCom)
+ if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
return new CompileResult(false);
}
- if (patchCom)
- ComInteropHelper.PatchAssembly(asm, typeSystem);
-
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
@@ -112,9 +109,8 @@ namespace Avalonia.Build.Tasks
}
}
var asm = typeSystem.TargetAssemblyDefinition;
- var emres = new EmbeddedResources(asm);
var avares = new AvaloniaResources(asm, projectDirectory);
- if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
+ if (avares.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return null;
@@ -436,9 +432,6 @@ namespace Avalonia.Build.Tasks
return true;
}
- if (emres.Resources.Count(CheckXamlName) != 0)
- if (!CompileGroup(emres))
- return false;
if (avares.Resources.Count(CheckXamlName) != 0)
{
if (!CompileGroup(avares))
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
index 39369bcbdb..29f9f3c571 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Controls
+using Avalonia.Controls.Primitives;
+
+namespace Avalonia.Controls
{
///
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@@ -11,8 +13,33 @@
///
public ColorPicker() : base()
{
- // Completely ignore property changes here
- // The ColorView in the control template is responsible to manage this
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+
+ // Until this point the ColorPicker itself is responsible to process property updates.
+ // This, for example, syncs Color with HsvColor and updates primitive controls.
+ //
+ // However, when the template is created, hand-off this change processing to the
+ // ColorView within the control template itself. Remember ColorPicker derives from
+ // ColorView so we don't want two instances of the same logic fighting each other.
+ // It is best to hand-off to the ColorView in the control template because that is the
+ // primary point of user-interaction for the overall control. It also simplifies binding.
+ //
+ // Keep in mind this hand-off is not possible until the template controls are created
+ // which is done after the ColorPicker is instantiated. The ColorPicker must still
+ // process updates before the template is applied to ensure all property changes in
+ // XAML or object initializers are handled correctly. Otherwise, there can be bugs
+ // such as setting the Color property doesn't work because the HsvColor is never updated
+ // and then the Color value is lost once the template loads (and the template ColorView
+ // takes over).
+ //
+ // In order to complete this hand-off, completely ignore property changes here in the
+ // ColorPicker. This means the ColorView in the control template is now responsible to
+ // process property changes and handle primary calculations.
base.ignorePropertyChanged = true;
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
index fab2b35969..be14073240 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
@@ -2,6 +2,7 @@
using Avalonia.Controls.Metadata;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Media.Imaging;
using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
@@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives
protected bool ignorePropertyChanged = false;
+ private WriteableBitmap? _backgroundBitmap;
+
///
/// Initializes a new instance of the class.
///
@@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ }
+
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ }
+
///
/// Updates the visual state of the control by applying latest PseudoClasses.
///
@@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
- var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
+ ArrayList bgraPixelData = await ColorPickerHelpers.CreateComponentBitmapAsync(
pixelWidth,
pixelHeight,
Orientation,
@@ -108,9 +123,27 @@ namespace Avalonia.Controls.Primitives
IsAlphaMaxForced,
IsSaturationValueMaxForced);
- if (bitmap != null)
+ if (bgraPixelData != null)
{
- Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight));
+ if (_backgroundBitmap != null)
+ {
+ // TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
+ //
+ // Re-use the existing WriteableBitmap
+ // This assumes the height, width and byte counts are the same and must be set to null
+ // elsewhere if that assumption is ever not true.
+ // ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData);
+
+ // TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
+ //_backgroundBitmap?.Dispose();
+ _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
+ }
+ else
+ {
+ _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
+ }
+
+ Background = new ImageBrush(_backgroundBitmap);
}
}
}
@@ -350,11 +383,11 @@ namespace Avalonia.Controls.Primitives
return;
}
- // Always keep the two color properties in sync
if (change.Property == ColorProperty)
{
ignorePropertyChanged = true;
+ // Always keep the two color properties in sync
HsvColor = Color.ToHsv();
SetColorToSliderValues();
@@ -367,7 +400,10 @@ namespace Avalonia.Controls.Primitives
ignorePropertyChanged = false;
}
- else if (change.Property == ColorModelProperty)
+ else if (change.Property == ColorComponentProperty ||
+ change.Property == ColorModelProperty ||
+ change.Property == IsAlphaMaxForcedProperty ||
+ change.Property == IsSaturationValueMaxForcedProperty)
{
ignorePropertyChanged = true;
@@ -381,6 +417,7 @@ namespace Avalonia.Controls.Primitives
{
ignorePropertyChanged = true;
+ // Always keep the two color properties in sync
Color = HsvColor.ToRgb();
SetColorToSliderValues();
@@ -399,7 +436,13 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == BoundsProperty)
{
+ // If the control's overall dimensions have changed the background bitmap size also needs to change.
+ // This means the existing bitmap must be released to be recreated correctly in UpdateBackground().
+ _backgroundBitmap?.Dispose();
+ _backgroundBitmap = null;
+
UpdateBackground();
+ UpdatePseudoClasses();
}
else if (change.Property == ValueProperty ||
change.Property == MinimumProperty ||
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
index 00d84f5dd3..39b7b7f660 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
@@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ThirdComponentProperty =
+ AvaloniaProperty.Register(
+ nameof(ThirdComponent),
+ ColorComponent.Component3); // Value
+
///
/// Gets or sets the currently selected color in the RGB color model.
///
@@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
+
+ ///
+ /// Gets the third HSV color component that is NOT displayed by the spectrum.
+ /// This is automatically calculated from the property.
+ ///
+ ///
+ /// This property should be used for any external color slider that represents the
+ /// third component of the color. Note that this property uses the generic
+ /// type instead of the more accurate
+ /// to allow direct usage by the generalized color sliders.
+ ///
+ public ColorComponent ThirdComponent
+ {
+ get => GetValue(ThirdComponentProperty);
+ private set => SetValue(ThirdComponentProperty, value);
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
index 1ff1445d8e..dd1f985a49 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
@@ -73,6 +73,8 @@ namespace Avalonia.Controls.Primitives
private WriteableBitmap? _saturationMaximumBitmap;
private WriteableBitmap? _valueBitmap;
+ private WriteableBitmap? _minBitmap;
+ private WriteableBitmap? _maxBitmap;
// Fields used by UpdateEllipse() to ensure that it's using the data
// associated with the last call to CreateBitmapsAndColorMap(),
@@ -95,7 +97,7 @@ namespace Avalonia.Controls.Primitives
///
/// Initializes a new instance of the class.
///
- public ColorSpectrum()
+ public ColorSpectrum() : base()
{
_shapeFromLastBitmapCreation = Shape;
_componentsFromLastBitmapCreation = Components;
@@ -171,6 +173,18 @@ namespace Avalonia.Controls.Primitives
{
base.OnAttachedToVisualTree(e);
+ // If the color was updated while this ColorSpectrum was not part of the visual tree,
+ // the selection ellipse may be in an incorrect position. This is because the spectrum
+ // renders based on layout scaling to avoid color banding; however, layout scale is only
+ // available when the control is attached to the visual tree. The ColorSpectrum's color
+ // may be updated from code-behind or from binding with another control when it's not
+ // part of the visual tree.
+ //
+ // See discussion: https://github.com/AvaloniaUI/Avalonia/discussions/9077
+ //
+ // To work-around this issue the selection ellipse is refreshed here.
+ UpdateEllipse();
+
// OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here
}
@@ -489,6 +503,23 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == ComponentsProperty)
{
+ // Calculate and update the ThirdComponent value
+ switch (Components)
+ {
+ case ColorSpectrumComponents.HueSaturation:
+ case ColorSpectrumComponents.SaturationHue:
+ ThirdComponent = (ColorComponent)HsvComponent.Value;
+ break;
+ case ColorSpectrumComponents.HueValue:
+ case ColorSpectrumComponents.ValueHue:
+ ThirdComponent = (ColorComponent)HsvComponent.Saturation;
+ break;
+ case ColorSpectrumComponents.SaturationValue:
+ case ColorSpectrumComponents.ValueSaturation:
+ ThirdComponent = (ColorComponent)HsvComponent.Hue;
+ break;
+ }
+
CreateBitmapsAndColorMap();
}
@@ -588,6 +619,10 @@ namespace Avalonia.Controls.Primitives
RaiseColorChanged();
}
+ ///
+ /// Updates the selected and based on a point within the color spectrum.
+ ///
+ /// The point on the spectrum representing the color.
private void UpdateColorFromPoint(PointerPoint point)
{
// If we haven't initialized our HSV value array yet, then we should just ignore any user input -
@@ -664,6 +699,9 @@ namespace Avalonia.Controls.Primitives
UpdateColor(hsvAtPoint);
}
+ ///
+ /// Updates the position of the selection ellipse on the spectrum which indicates the selected color.
+ ///
private void UpdateEllipse()
{
if (_selectionEllipsePanel == null)
@@ -832,6 +870,8 @@ namespace Avalonia.Controls.Primitives
}
// Remember the bitmap size follows physical device pixels
+ // Warning: LayoutHelper.GetLayoutScale() doesn't work unless the control is visible
+ // This will not be true in all cases if the color is updated from another control or code-behind
var scale = LayoutHelper.GetLayoutScale(this);
Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2));
Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2));
@@ -973,13 +1013,13 @@ namespace Avalonia.Controls.Primitives
// The middle 4 are only needed and used in the case of hue as the third dimension.
// Saturation and luminosity need only a min and max.
- List bgraMinPixelData = new List();
- List bgraMiddle1PixelData = new List();
- List bgraMiddle2PixelData = new List();
- List bgraMiddle3PixelData = new List();
- List bgraMiddle4PixelData = new List();
- List bgraMaxPixelData = new List();
- List newHsvValues = new List();
+ ArrayList bgraMinPixelData;
+ ArrayList bgraMiddle1PixelData;
+ ArrayList bgraMiddle2PixelData;
+ ArrayList bgraMiddle3PixelData;
+ ArrayList bgraMiddle4PixelData;
+ ArrayList bgraMaxPixelData;
+ List newHsvValues;
// In Avalonia, Bounds returns the actual device-independent pixel size of a control.
// However, this is not necessarily the size of the control rendered on a display.
@@ -990,20 +1030,27 @@ namespace Avalonia.Controls.Primitives
int pixelDimension = (int)Math.Round(minDimension * scale);
var pixelCount = pixelDimension * pixelDimension;
var pixelDataSize = pixelCount * 4;
- bgraMinPixelData.Capacity = pixelDataSize;
+
+ bgraMinPixelData = new ArrayList(pixelDataSize);
+ bgraMaxPixelData = new ArrayList(pixelDataSize);
+ newHsvValues = new List(pixelCount);
// We'll only save pixel data for the middle bitmaps if our third dimension is hue.
if (components == ColorSpectrumComponents.ValueSaturation ||
components == ColorSpectrumComponents.SaturationValue)
{
- bgraMiddle1PixelData.Capacity = pixelDataSize;
- bgraMiddle2PixelData.Capacity = pixelDataSize;
- bgraMiddle3PixelData.Capacity = pixelDataSize;
- bgraMiddle4PixelData.Capacity = pixelDataSize;
+ bgraMiddle1PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle2PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle3PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle4PixelData = new ArrayList(pixelDataSize);
+ }
+ else
+ {
+ bgraMiddle1PixelData = new ArrayList(0);
+ bgraMiddle2PixelData = new ArrayList(0);
+ bgraMiddle3PixelData = new ArrayList(0);
+ bgraMiddle4PixelData = new ArrayList(0);
}
-
- bgraMaxPixelData.Capacity = pixelDataSize;
- newHsvValues.Capacity = pixelCount;
await Task.Run(() =>
{
@@ -1056,28 +1103,28 @@ namespace Avalonia.Controls.Primitives
ColorSpectrumComponents components2 = Components;
- WriteableBitmap minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
- WriteableBitmap maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
+ _minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
+ _maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
switch (components2)
{
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
- _saturationMinimumBitmap = minBitmap;
- _saturationMaximumBitmap = maxBitmap;
+ _saturationMinimumBitmap = _minBitmap;
+ _saturationMaximumBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
- _valueBitmap = maxBitmap;
+ _valueBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.ValueSaturation:
case ColorSpectrumComponents.SaturationValue:
- _hueRedBitmap = minBitmap;
+ _hueRedBitmap = _minBitmap;
_hueYellowBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle1PixelData, pixelWidth, pixelHeight);
_hueGreenBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle2PixelData, pixelWidth, pixelHeight);
_hueCyanBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle3PixelData, pixelWidth, pixelHeight);
_hueBlueBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle4PixelData, pixelWidth, pixelHeight);
- _huePurpleBitmap = maxBitmap;
+ _huePurpleBitmap = _maxBitmap;
break;
}
@@ -1111,12 +1158,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
- List bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
@@ -1271,12 +1318,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
- List bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
diff --git a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
deleted file mode 100644
index 11e33c74f0..0000000000
--- a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace Avalonia.Controls.Primitives.Converters
-{
- ///
- /// Gets the third corresponding with a given
- /// that represents the other two components.
- ///
- ///
- /// This is a highly-specialized converter for the color picker.
- ///
- public class ThirdComponentConverter : IValueConverter
- {
- ///
- public object? Convert(
- object? value,
- Type targetType,
- object? parameter,
- CultureInfo culture)
- {
- if (value is ColorSpectrumComponents components)
- {
- // Note: Alpha is not relevant here
- switch (components)
- {
- case ColorSpectrumComponents.HueSaturation:
- case ColorSpectrumComponents.SaturationHue:
- return (ColorComponent)HsvComponent.Value;
- case ColorSpectrumComponents.HueValue:
- case ColorSpectrumComponents.ValueHue:
- return (ColorComponent)HsvComponent.Saturation;
- case ColorSpectrumComponents.SaturationValue:
- case ColorSpectrumComponents.ValueSaturation:
- return (ColorComponent)HsvComponent.Hue;
- }
- }
-
- return AvaloniaProperty.UnsetValue;
- }
-
- ///
- public object? ConvertBack(
- object? value,
- Type targetType,
- object? parameter,
- CultureInfo culture)
- {
- return AvaloniaProperty.UnsetValue;
- }
- }
-}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
new file mode 100644
index 0000000000..0b4c2f8579
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
@@ -0,0 +1,71 @@
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// A thin wrapper over an that allows some additional list-like functionality.
+ ///
+ ///
+ /// This is only for internal ColorPicker-related functionality and should not be used elsewhere.
+ /// It is added for performance to enjoy the simplicity of the IList.Add() method without requiring
+ /// an additional copy to turn a list into an array for bitmaps.
+ ///
+ /// The type of items in the array.
+ internal class ArrayList
+ {
+ private int _nextIndex = 0;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ArrayList(int capacity)
+ {
+ Capacity = capacity;
+ Array = new T[capacity];
+ }
+
+ ///
+ /// Provides access to the underlying array by index.
+ /// This exists for simplification and the property
+ /// may also be used.
+ ///
+ /// The index of the item to get or set.
+ /// The item at the given index.
+ public T this[int i]
+ {
+ get => Array[i];
+ set => Array[i] = value;
+ }
+
+ ///
+ /// Gets the underlying array.
+ ///
+ public T[] Array { get; private set; }
+
+ ///
+ /// Gets the fixed capacity/size of the array.
+ /// This must be set during construction.
+ ///
+ public int Capacity { get; private set; }
+
+ ///
+ /// Adds the given item to the array at the next available index.
+ /// WARNING: This must be used carefully and only once, in sequence.
+ ///
+ /// The item to add.
+ public void Add(T item)
+ {
+ if (_nextIndex >= 0 &&
+ _nextIndex < Capacity)
+ {
+ Array[_nextIndex] = item;
+ _nextIndex++;
+ }
+ else
+ {
+ // If necessary an exception could be thrown here
+ // throw new IndexOutOfRangeException();
+ }
+
+ return;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
index 381bc42aaa..819d745772 100644
--- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Controls.Primitives
/// during calculation with the HSVA color model.
/// This will ensure colors are always discernible regardless of saturation/value.
/// A new bitmap representing a gradient of color component values.
- public static async Task CreateComponentBitmapAsync(
+ public static async Task> CreateComponentBitmapAsync(
int width,
int height,
Orientation orientation,
@@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
- return Array.Empty();
+ return new ArrayList(0);
}
- var bitmap = await Task.Run(() =>
+ var bitmap = await Task.Run>(() =>
{
int pixelDataIndex = 0;
double componentStep;
- byte[] bgraPixelData;
+ ArrayList bgraPixelData;
Color baseRgbColor = Colors.White;
Color rgbColor;
int bgraPixelDataHeight;
@@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
// Allocate the buffer
// BGRA formatted color components 1 byte each (4 bytes in a pixel)
- bgraPixelData = new byte[width * height * 4];
+ bgraPixelData = new ArrayList(width * height * 4);
bgraPixelDataHeight = height * 4;
bgraPixelDataWidth = width * 4;
@@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives
/// The pixel height of the bitmap.
/// A new .
public static WriteableBitmap CreateBitmapFromPixelData(
- IList bgraPixelData,
+ ArrayList bgraPixelData,
int pixelWidth,
int pixelHeight)
{
@@ -617,13 +617,31 @@ namespace Avalonia.Controls.Primitives
PixelFormat.Bgra8888,
AlphaFormat.Premul);
- // Warning: This is highly questionable
using (var frameBuffer = bitmap.Lock())
{
- Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
+ Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return bitmap;
}
+
+ ///
+ /// Updates the given with new, raw BGRA pre-multiplied alpha pixel data.
+ /// TODO: THIS METHOD IS CURRENTLY PROVIDED AS REFERENCE BUT CAUSES INTERMITTENT CRASHES IF USED.
+ /// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally.
+ ///
+ /// The existing to update.
+ /// The bitmap (in raw BGRA pre-multiplied alpha pixels).
+ public static void UpdateBitmapFromPixelData(
+ WriteableBitmap bitmap,
+ ArrayList bgraPixelData)
+ {
+ using (var frameBuffer = bitmap.Lock())
+ {
+ Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
+ }
+
+ return;
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
index 16bc2acdd1..b3c6d1a430 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
@@ -8,7 +8,6 @@
x:CompileBindings="True">
-
@@ -241,23 +240,21 @@
-
+
-
+
-
@@ -215,23 +214,21 @@
-
+
-
+
()
- where attribute.ForWindowingSubsystem == ""
+ where string.IsNullOrEmpty(attribute.ForWindowingSubsystem)
|| attribute.ForWindowingSubsystem == WindowingSubsystemName
- where attribute.ForRenderingSubsystem == ""
+ where string.IsNullOrEmpty(attribute.ForRenderingSubsystem)
|| attribute.ForRenderingSubsystem == RenderingSubsystemName
group attribute by attribute.Name into exports
select (from export in exports
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index 228b9ae205..a7a6881fe5 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -99,7 +99,7 @@ namespace Avalonia.Controls
get => _clockIdentifier;
set
{
- if (!(string.IsNullOrEmpty(value) || value == "" || value == "12HourClock" || value == "24HourClock"))
+ if (!(string.IsNullOrEmpty(value) || value == "12HourClock" || value == "24HourClock"))
throw new ArgumentException("Invalid ClockIdentifier");
SetAndRaise(ClockIdentifierProperty, ref _clockIdentifier, value);
SetGrid();
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index eb09ff397a..64a02ccb46 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -366,7 +366,7 @@ namespace Avalonia.Controls
string id = (string)value;
- if (id != string.Empty)
+ if (!string.IsNullOrEmpty(id))
{
int i = -1;
while (++i < id.Length)
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index 00ebcab70e..65ec4cc54c 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -597,7 +597,7 @@ namespace Avalonia.Controls.Primitives
for (int i = presenter.Classes.Count - 1; i >= 0; i--)
{
if (!classes.Contains(presenter.Classes[i]) &&
- !presenter.Classes[i].Contains(":"))
+ !presenter.Classes[i].Contains(':'))
{
presenter.Classes.RemoveAt(i);
}
diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
index b607ced10a..21373c6b76 100644
--- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
+++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
@@ -1151,8 +1151,8 @@ namespace Avalonia.Controls
if (PIndex >= 0)
{
//stringToTest contains a "P" between 2 "'", it's considered as text, not percent
- var isText = stringToTest.Substring(0, PIndex).Contains("'")
- && stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains("'");
+ var isText = stringToTest.Substring(0, PIndex).Contains('\'')
+ && stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains('\'');
return !isText;
}
diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs
index 9a872df960..6bfae536c9 100644
--- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs
+++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs
@@ -72,7 +72,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
while (true)
{
line = await ReadLineAsync();
- if (line == "")
+ if (string.IsNullOrEmpty(line))
break;
sp = line.Split(new[] {':'}, 2);
headers[sp[0]] = sp[1].TrimStart();
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
index 1270dbaa62..d2b31fdb20 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
@@ -3,8 +3,7 @@
xmlns:views="clr-namespace:Avalonia.Diagnostics.Views"
xmlns:diag="clr-namespace:Avalonia.Diagnostics"
Title="Avalonia DevTools"
- x:Class="Avalonia.Diagnostics.Views.MainWindow"
- Theme="{StaticResource {x:Type Window}}">
+ x:Class="Avalonia.Diagnostics.Views.MainWindow">
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index c81997f2cb..e6e630112b 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -28,6 +28,11 @@ namespace Avalonia.Diagnostics.Views
{
InitializeComponent();
+ // Apply the SimpleTheme.Window theme; this must be done after the XAML is parsed as
+ // the theme is included in the MainWindow's XAML.
+ if (Theme is null && this.FindResource(typeof(Window)) is ControlTheme windowTheme)
+ Theme = windowTheme;
+
_keySubscription = InputManager.Instance?.Process
.OfType()
.Where(x => x.Type == RawKeyEventType.KeyDown)
diff --git a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
index d7f39f6642..599d95e67a 100644
--- a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
+++ b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
@@ -3,9 +3,6 @@
netstandard2.0
true
-
- <_AvaloniaPatchComInterop>true
-
false
diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs
index 524ec09ca9..6abece6bf3 100644
--- a/src/Avalonia.Remote.Protocol/MetsysBson.cs
+++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs
@@ -1364,13 +1364,13 @@ namespace Metsys.Bson
var optionsString = ReadName();
var options = RegexOptions.None;
- if (optionsString.Contains("e")) options = options | RegexOptions.ECMAScript;
- if (optionsString.Contains("i")) options = options | RegexOptions.IgnoreCase;
- if (optionsString.Contains("l")) options = options | RegexOptions.CultureInvariant;
- if (optionsString.Contains("m")) options = options | RegexOptions.Multiline;
- if (optionsString.Contains("s")) options = options | RegexOptions.Singleline;
- if (optionsString.Contains("w")) options = options | RegexOptions.IgnorePatternWhitespace;
- if (optionsString.Contains("x")) options = options | RegexOptions.ExplicitCapture;
+ if (optionsString.Contains('e')) options = options | RegexOptions.ECMAScript;
+ if (optionsString.Contains('i')) options = options | RegexOptions.IgnoreCase;
+ if (optionsString.Contains('l')) options = options | RegexOptions.CultureInvariant;
+ if (optionsString.Contains('m')) options = options | RegexOptions.Multiline;
+ if (optionsString.Contains('s')) options = options | RegexOptions.Singleline;
+ if (optionsString.Contains('w')) options = options | RegexOptions.IgnorePatternWhitespace;
+ if (optionsString.Contains('x')) options = options | RegexOptions.ExplicitCapture;
return new Regex(pattern, options);
}
diff --git a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
index c91e008e02..71ae012289 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
@@ -83,14 +83,14 @@
-
-
-
diff --git a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml
index 379b92ea17..4522ffac87 100644
--- a/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml
@@ -92,14 +92,14 @@
-
-
-
diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs
index 23abd31b2c..3f0a6aeb67 100644
--- a/src/Avalonia.X11/X11Structs.cs
+++ b/src/Avalonia.X11/X11Structs.cs
@@ -661,7 +661,7 @@ namespace Avalonia.X11 {
Type type = ev.GetType ();
FieldInfo [] fields = type.GetFields (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++) {
- if (result != string.Empty) {
+ if (!string.IsNullOrEmpty(result)) {
result += ", ";
}
object value = fields [i].GetValue (ev);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index debdece60f..f9be3fd62a 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -5,7 +5,7 @@
true
Avalonia.Markup.Xaml.Loader
$(DefineConstants);XAMLX_INTERNAL
- 10
+ 11
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
index 1e42a46875..1b6fbcef5c 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.Converters
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var valueStr = (string)value;
- if (!valueStr.Contains(":"))
+ if (!valueStr.Contains(':'))
{
// shorthand seconds format (ie. "0.25")
var secs = double.Parse(valueStr, CultureInfo.InvariantCulture);
@@ -25,4 +25,4 @@ namespace Avalonia.Markup.Xaml.Converters
return base.ConvertFrom(context, culture, value);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
index d462a2210e..f28f7bc626 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
@@ -56,7 +56,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// We need to implement compile-time merging of resource dictionaries and this
// hack can be removed.
if (previousWasControlTheme &&
- parent is ResourceDictionary hack &&
+ parent is IResourceProvider hack &&
hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
hack.Owner.TryGetResource(ResourceKey, out value))
{
diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
index 6711c3dd3d..6b25cbbeab 100644
--- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
+++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
@@ -2,6 +2,7 @@
net6.0;netstandard2.0
Avalonia
+ 11
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
index 439bc15243..0a9fbcfacb 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
@@ -168,7 +168,7 @@ namespace Avalonia.Markup.Parsers
}
}
- private static State ParseAttachedProperty(ref CharacterReader r, List nodes)
+ private static State ParseAttachedProperty(scoped ref CharacterReader r, List nodes)
{
var (ns, owner) = ParseTypeName(ref r);
@@ -318,7 +318,7 @@ namespace Avalonia.Markup.Parsers
return State.AfterMember;
}
- private static TypeName ParseTypeName(ref CharacterReader r)
+ private static TypeName ParseTypeName(scoped ref CharacterReader r)
{
ReadOnlySpan ns, typeName;
ns = ReadOnlySpan.Empty;
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
index 16856e674d..4d6d16a3ce 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
@@ -376,28 +376,28 @@ namespace Avalonia.Markup.Parsers
if (r.Peek == 'o')
{
- var constArg = r.TakeUntil(')').ToString().Trim();
- if (constArg.Equals("odd", StringComparison.Ordinal))
+ var constArg = r.TakeUntil(')').Trim();
+ if (constArg.SequenceEqual("odd".AsSpan()))
{
step = 2;
offset = 1;
}
else
{
- throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg}'.");
+ throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg.ToString()}'.");
}
}
else if (r.Peek == 'e')
{
- var constArg = r.TakeUntil(')').ToString().Trim();
- if (constArg.Equals("even", StringComparison.Ordinal))
+ var constArg = r.TakeUntil(')').Trim();
+ if (constArg.SequenceEqual("even".AsSpan()))
{
step = 2;
offset = 0;
}
else
{
- throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg}'.");
+ throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg.ToString()}'.");
}
}
else
@@ -405,7 +405,7 @@ namespace Avalonia.Markup.Parsers
r.SkipWhitespace();
var stepOrOffset = 0;
- var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+').ToString();
+ var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+');
if (stepOrOffsetStr.Length == 0
|| (stepOrOffsetStr.Length == 1
&& stepOrOffsetStr[0] == '+'))
@@ -417,7 +417,7 @@ namespace Avalonia.Markup.Parsers
{
stepOrOffset = -1;
}
- else if (!int.TryParse(stepOrOffsetStr.ToString(), out stepOrOffset))
+ else if (!stepOrOffsetStr.TryParseInt(out stepOrOffset))
{
throw new ExpressionParseException(r.Position, "Couldn't parse nth-child step or offset value. Integer was expected.");
}
@@ -462,7 +462,7 @@ namespace Avalonia.Markup.Parsers
r.SkipWhitespace();
if (sign != 0
- && !int.TryParse(r.TakeUntil(')').ToString(), out offset))
+ && !r.TakeUntil(')').TryParseInt(out offset))
{
throw new ExpressionParseException(r.Position, "Couldn't parse nth-child offset value. Integer was expected.");
}
diff --git a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs
index f6616d74a2..029f8e236c 100644
--- a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakTestDataGenerator.cs
@@ -80,7 +80,7 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
var remaining = remainingChars.Where(x => x != "" && x != "×").Select(x => Convert.ToInt32(x, 16)).ToArray();
codepoints.AddRange(remaining);
- }
+ }
var data = new GraphemeBreakData
{
diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
index 3f4978f544..754a1d6a24 100644
--- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
+++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
@@ -1,6 +1,5 @@
- Exe
net6.0
Exe
false
diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
index 278b0e087e..a91cf3911a 100644
--- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
+++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
@@ -8,9 +8,9 @@
%(Filename)
-
+
Designer
-
+
diff --git a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
index 7eeab2f7f2..2dd849bee1 100644
--- a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
@@ -109,7 +109,7 @@ namespace Avalonia.IntegrationTests.Appium
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual))
{
- mainWindow.Click();
+ mainWindow.SendClick();
var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
Assert.Equal(1, secondaryWindowIndex);
}
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
new file mode 100644
index 0000000000..52af0f5ab2
--- /dev/null
+++ b/tests/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+
+ false
+
+