diff --git a/.editorconfig b/.editorconfig index 9014c8938f..b4c617e0bb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -143,6 +143,8 @@ dotnet_diagnostic.CA1802.severity = warning dotnet_diagnostic.CA1825.severity = warning # CA1821: Remove empty finalizers dotnet_diagnostic.CA1821.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/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.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/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 b662d20223..ec08e96d87 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 bd44161a42..f0ed89fb3a 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 @@ - + - + = 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.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/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/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 + +