From bbf3099a8f9f482f851f6cfe497f22c8ca95bbcf Mon Sep 17 00:00:00 2001 From: Adir Hudayfi Date: Fri, 8 Apr 2022 14:34:01 +0300 Subject: [PATCH 01/22] Fixed memory leak when DataGrid is attached to INotifyCollectionChanged --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 19 +++++++++ .../Avalonia.LeakTests.csproj | 1 + tests/Avalonia.LeakTests/ControlTests.cs | 42 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 5d71a499e3..9dfb34517b 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -2060,6 +2060,25 @@ namespace Avalonia.Controls forceHorizontalScroll: true); } } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + if (DataConnection.DataSource != null && !DataConnection.EventsWired) + { + DataConnection.WireEvents(DataConnection.DataSource); + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + // When wired to INotifyCollectionChanged, the DataGrid will be cleaned up by GC + if (DataConnection.DataSource != null && DataConnection.EventsWired) + { + DataConnection.UnWireEvents(DataConnection.DataSource); + } + } /// /// Arranges the content of the . diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index d49a859b89..7e569f2eac 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 087d42370e..3a5a8f1474 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Runtime.Remoting.Contexts; using Avalonia.Controls; @@ -24,11 +25,52 @@ namespace Avalonia.LeakTests [DotMemoryUnit(FailIfRunWithoutSupport = false)] public class ControlTests { + // Need to have the collection as field, so GC will not free it + private readonly ObservableCollection _observableCollection = new(); + public ControlTests(ITestOutputHelper atr) { DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine); } + + [Fact] + public void DataGrid_Is_Freed() + { + using (Start()) + { + // When attached to INotifyCollectionChanged, DataGrid will subscribe to it's events, potentially causing leak + Func run = () => + { + var window = new Window + { + Content = new DataGrid + { + Items = _observableCollection + } + }; + + window.Show(); + + // Do a layout and make sure that DataGrid gets added to visual tree. + window.LayoutManager.ExecuteInitialLayoutPass(); + Assert.IsType(window.Presenter.Child); + + // Clear the content and ensure the DataGrid is removed. + window.Content = null; + window.LayoutManager.ExecuteLayoutPass(); + Assert.Null(window.Presenter.Child); + + return window; + }; + + var result = run(); + + dotMemory.Check(memory => + Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); + } + } + [Fact] public void Canvas_Is_Freed() { From b6ec588b4b523a789defbad7927500abd41f1d68 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 8 Apr 2022 22:35:01 -0400 Subject: [PATCH 02/22] Rename 'channel' to 'component' --- src/Avalonia.Visuals/Media/HsvColor.cs | 68 +++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 4a0277b4d4..25180e550d 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -21,11 +21,11 @@ namespace Avalonia.Media /// /// Initializes a new instance of the struct. /// - /// The Alpha (transparency) channel value in the range from 0..1. - /// The Hue channel value in the range from 0..360. + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. public HsvColor( double alpha, double hue, @@ -49,14 +49,14 @@ namespace Avalonia.Media /// /// /// This constructor exists only for internal use where performance is critical. - /// Whether or not the channel values are in the correct ranges must be known. + /// Whether or not the component values are in the correct ranges must be known. /// - /// The Alpha (transparency) channel value in the range from 0..1. - /// The Hue channel value in the range from 0..360. + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. - /// Whether to clamp channel values to their required ranges. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. + /// Whether to clamp component values to their required ranges. internal HsvColor( double alpha, double hue, @@ -98,23 +98,23 @@ namespace Avalonia.Media } /// - /// Gets the Alpha (transparency) channel value in the range from 0..1. + /// Gets the Alpha (transparency) component in the range from 0..1. /// public double A { get; } /// - /// Gets the Hue channel value in the range from 0..360. + /// Gets the Hue component in the range from 0..360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. /// public double H { get; } /// - /// Gets the Saturation channel value in the range from 0..1. + /// Gets the Saturation component in the range from 0..1. /// public double S { get; } /// - /// Gets the Value channel value in the range from 0..1. + /// Gets the Value component in the range from 0..1. /// public double V { get; } @@ -165,7 +165,7 @@ namespace Avalonia.Media /// The RGB equivalent color. public Color ToRgb() { - // Use the by-channel conversion method directly for performance + // Use the by-component conversion method directly for performance return HsvColor.ToRgb(H, S, V, A); } @@ -293,17 +293,17 @@ namespace Avalonia.Media } /// - /// Creates a new from individual color channel values. + /// Creates a new from individual color component values. /// /// /// This exists for symmetry with the struct; however, the /// appropriate constructor should commonly be used instead. /// - /// The Alpha (transparency) channel value in the range from 0..1. - /// The Hue channel value in the range from 0..360. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. - /// A new built from the individual color channel values. + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. + /// A new built from the individual color component values. public static HsvColor FromAhsv(double a, double h, double s, double v) { return new HsvColor(a, h, s, v); @@ -320,12 +320,12 @@ namespace Avalonia.Media } /// - /// Converts the given HSVA color channel values to it's RGB color equivalent. + /// Converts the given HSVA color component values to it's RGB color equivalent. /// - /// The hue channel value in the HSV color model in the range from 0..360. - /// The saturation channel value in the HSV color model in the range from 0..1. - /// The value channel value in the HSV color model in the range from 0..1. - /// The alpha channel value in the range from 0..1. + /// The hue component in the HSV color model in the range from 0..360. + /// The saturation component in the HSV color model in the range from 0..1. + /// The value component in the HSV color model in the range from 0..1. + /// The alpha component in the range from 0..1. /// A new RGB equivalent to the given HSVA values. public static Color ToRgb( double hue, @@ -336,7 +336,7 @@ namespace Avalonia.Media // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp // This was used because it is the best documented and likely most optimized for performance - // Alpha channel support was added + // Alpha support was added // We want the hue to be between 0 and 359, // so we first ensure that that's the case. @@ -468,12 +468,12 @@ namespace Avalonia.Media } /// - /// Converts the given RGBA color channel values to it's HSV color equivalent. + /// Converts the given RGBA color component values to its HSV color equivalent. /// - /// The red channel value in the RGB color model. - /// The green channel value in the RGB color model. - /// The blue channel value in the RGB color model. - /// The alpha channel value. + /// The red component in the RGB color model. + /// The green component in the RGB color model. + /// The blue component in the RGB color model. + /// The alpha component. /// A new equivalent to the given RGBA values. public static HsvColor FromRgb( byte red, @@ -484,9 +484,9 @@ namespace Avalonia.Media // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp // This was used because it is the best documented and likely most optimized for performance - // Alpha channel support was added + // Alpha support was added - // Normalize RGBA channel values into the 0..1 range used by this algorithm + // Normalize RGBA components into the 0..1 range used by this algorithm double r = red / 255.0; double g = green / 255.0; double b = blue / 255.0; From 3527baaefa48ec37b46533d72ee602bc059667e1 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 8 Apr 2022 22:37:59 -0400 Subject: [PATCH 03/22] Add back HsvColor.FromHsv() --- src/Avalonia.Visuals/Media/Color.cs | 4 +++- src/Avalonia.Visuals/Media/HsvColor.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index aa730b3219..9f8588d400 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -278,7 +278,7 @@ namespace Avalonia.Media /// The HSV equivalent color. public HsvColor ToHsv() { - // Use the by-channel conversion method directly for performance + // Use the by-component conversion method directly for performance // Don't use the HsvColor(Color) constructor to avoid an extra HsvColor return HsvColor.FromRgb(R, G, B, A); } @@ -289,11 +289,13 @@ namespace Avalonia.Media return A == other.A && R == other.R && G == other.G && B == other.B; } + /// public override bool Equals(object? obj) { return obj is Color other && Equals(other); } + /// public override int GetHashCode() { unchecked diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 25180e550d..c58db9df00 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -309,6 +309,22 @@ namespace Avalonia.Media return new HsvColor(a, h, s, v); } + /// + /// Creates a new from individual color component values. + /// + /// + /// This exists for symmetry with the struct; however, the + /// appropriate constructor should commonly be used instead. + /// + /// The Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. + /// A new built from the individual color component values. + public static HsvColor FromHsv(double h, double s, double v) + { + return new HsvColor(1.0, h, s, v); + } + /// /// Converts the given HSV color to it's RGB color equivalent. /// From 1c019eb21136c6e134b1cf948cd497ea1a52ba76 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 8 Apr 2022 23:51:49 -0400 Subject: [PATCH 04/22] Add new HslColor struct --- .../Avalonia.Build.Tasks.csproj | 3 + src/Avalonia.Visuals/Media/Color.cs | 12 + src/Avalonia.Visuals/Media/HslColor.cs | 322 ++++++++++++++++++ src/Avalonia.Visuals/Media/HsvColor.cs | 16 +- 4 files changed, 345 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/HslColor.cs diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 5a7daa6d12..9629324c8d 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -83,6 +83,9 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + Markup/%(RecursiveDir)%(FileName)%(Extension) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 9f8588d400..84fde9bfc2 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -308,11 +308,23 @@ namespace Avalonia.Media } } + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are equal; otherwise, false. public static bool operator ==(Color left, Color right) { return left.Equals(right); } + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are not equal; otherwise, false. public static bool operator !=(Color left, Color right) { return !left.Equals(right); diff --git a/src/Avalonia.Visuals/Media/HslColor.cs b/src/Avalonia.Visuals/Media/HslColor.cs new file mode 100644 index 0000000000..21ab669a05 --- /dev/null +++ b/src/Avalonia.Visuals/Media/HslColor.cs @@ -0,0 +1,322 @@ +// Color conversion portions of this source file are adapted from the Windows Community Toolkit project. +// (https://github.com/CommunityToolkit/WindowsCommunityToolkit) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. + +using System; +using System.Globalization; +using System.Text; +using Avalonia.Utilities; + +namespace Avalonia.Media +{ + /// + /// Defines a color using the hue/saturation/lightness (HSL) model. + /// +#if !BUILDTASK + public +#endif + readonly struct HslColor : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. + /// Note that 360 is equivalent to 0 and will be adjusted automatically. + /// The Saturation component in the range from 0..1. + /// The Lightness component in the range from 0..1. + public HslColor( + double alpha, + double hue, + double saturation, + double lightness) + { + A = MathUtilities.Clamp(alpha, 0.0, 1.0); + H = MathUtilities.Clamp(hue, 0.0, 360.0); + S = MathUtilities.Clamp(saturation, 0.0, 1.0); + L = MathUtilities.Clamp(lightness, 0.0, 1.0); + + // The maximum value of Hue is technically 360 minus epsilon (just below 360). + // This is because, in a color circle, 360 degrees is equivalent to 0 degrees. + // However, that is too tricky to work with in code and isn't as intuitive. + // Therefore, since 360 == 0, just wrap 360 if needed back to 0. + H = (H == 360.0 ? 0 : H); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// This constructor exists only for internal use where performance is critical. + /// Whether or not the component values are in the correct ranges must be known. + /// + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. + /// Note that 360 is equivalent to 0 and will be adjusted automatically. + /// The Saturation component in the range from 0..1. + /// The Lightness component in the range from 0..1. + /// Whether to clamp component values to their required ranges. + internal HslColor( + double alpha, + double hue, + double saturation, + double lightness, + bool clampValues) + { + if (clampValues) + { + A = MathUtilities.Clamp(alpha, 0.0, 1.0); + H = MathUtilities.Clamp(hue, 0.0, 360.0); + S = MathUtilities.Clamp(saturation, 0.0, 1.0); + L = MathUtilities.Clamp(lightness, 0.0, 1.0); + + // See comments in constructor above + H = (H == 360.0 ? 0 : H); + } + else + { + A = alpha; + H = hue; + S = saturation; + L = lightness; + } + } + + /// + /// Gets the Alpha (transparency) component in the range from 0..1. + /// + public double A { get; } + + /// + /// Gets the Hue component in the range from 0..360. + /// Note that 360 is equivalent to 0 and will be adjusted automatically. + /// + public double H { get; } + + /// + /// Gets the Saturation component in the range from 0..1. + /// + public double S { get; } + + /// + /// Gets the Lightness component in the range from 0..1. + /// + public double L { get; } + + /// + public bool Equals(HslColor other) + { + return other.A == A && + other.H == H && + other.S == S && + other.L == L; + } + + /// + public override bool Equals(object? obj) + { + if (obj is HslColor hslColor) + { + return Equals(hslColor); + } + else + { + return false; + } + } + + /// + /// Gets a hashcode for this object. + /// Hashcode is not guaranteed to be unique. + /// + /// The hashcode for this object. + public override int GetHashCode() + { + // Same algorithm as Color + // This is used instead of HashCode.Combine() due to .NET Standard 2.0 requirements + unchecked + { + int hashCode = A.GetHashCode(); + hashCode = (hashCode * 397) ^ H.GetHashCode(); + hashCode = (hashCode * 397) ^ S.GetHashCode(); + hashCode = (hashCode * 397) ^ L.GetHashCode(); + return hashCode; + } + } + + /// + /// Returns the RGB color model equivalent of this HSL color. + /// + /// The RGB equivalent color. + public Color ToRgb() + { + // Use the by-component conversion method directly for performance + return HslColor.ToRgb(H, S, L, A); + } + + /// + /// Creates a new from individual color component values. + /// + /// + /// This exists for symmetry with the struct; however, the + /// appropriate constructor should commonly be used instead. + /// + /// The Alpha (transparency) component in the range from 0..1. + /// The Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Lightness component in the range from 0..1. + /// A new built from the individual color component values. + public static HslColor FromAhsl(double a, double h, double s, double l) + { + return new HslColor(a, h, s, l); + } + + /// + /// Creates a new from individual color component values. + /// + /// + /// This exists for symmetry with the struct; however, the + /// appropriate constructor should commonly be used instead. + /// + /// The Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Lightness component in the range from 0..1. + /// A new built from the individual color component values. + public static HslColor FromHsl(double h, double s, double l) + { + return new HslColor(1.0, h, s, l); + } + + /// + /// Converts the given HSL color to it's RGB color equivalent. + /// + /// The color in the HSL color model. + /// A new RGB equivalent to the given HSLA values. + public static Color ToRgb(HslColor hslColor) + { + return HslColor.ToRgb(hslColor.H, hslColor.S, hslColor.L, hslColor.A); + } + + /// + /// Converts the given HSLA color component values to it's RGB color equivalent. + /// + /// The Hue component in the HSL color model in the range from 0..360. + /// The Saturation component in the HSL color model in the range from 0..1. + /// The Lightness component in the HSL color model in the range from 0..1. + /// The Alpha component in the range from 0..1. + /// A new RGB equivalent to the given HSLA values. + public static Color ToRgb( + double hue, + double saturation, + double lightness, + double alpha = 1.0) + { + // Note: Conversion code is originally based on ColorHelper in the Windows Community Toolkit (licensed MIT) + // https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp/Helpers/ColorHelper.cs + // It has been modified to ensure input ranges and not throw exceptions. + + // We want the hue to be between 0 and 359, + // so we first ensure that that's the case. + while (hue >= 360.0) + { + hue -= 360.0; + } + + while (hue < 0.0) + { + hue += 360.0; + } + + // We similarly clamp saturation, lightness and alpha between 0 and 1. + saturation = saturation < 0.0 ? 0.0 : saturation; + saturation = saturation > 1.0 ? 1.0 : saturation; + + lightness = lightness < 0.0 ? 0.0 : lightness; + lightness = lightness > 1.0 ? 1.0 : lightness; + + alpha = alpha < 0.0 ? 0.0 : alpha; + alpha = alpha > 1.0 ? 1.0 : alpha; + + double chroma = (1 - Math.Abs((2 * lightness) - 1)) * saturation; + double h1 = hue / 60; + double x = chroma * (1 - Math.Abs((h1 % 2) - 1)); + double m = lightness - (0.5 * chroma); + double r1, g1, b1; + + if (h1 < 1) + { + r1 = chroma; + g1 = x; + b1 = 0; + } + else if (h1 < 2) + { + r1 = x; + g1 = chroma; + b1 = 0; + } + else if (h1 < 3) + { + r1 = 0; + g1 = chroma; + b1 = x; + } + else if (h1 < 4) + { + r1 = 0; + g1 = x; + b1 = chroma; + } + else if (h1 < 5) + { + r1 = x; + g1 = 0; + b1 = chroma; + } + else + { + r1 = chroma; + g1 = 0; + b1 = x; + } + + return Color.FromArgb( + (byte)(255 * alpha), + (byte)(255 * (r1 + m)), + (byte)(255 * (g1 + m)), + (byte)(255 * (b1 + m))); + } + + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are equal; otherwise, false. + public static bool operator ==(HslColor left, HslColor right) + { + return left.Equals(right); + } + + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if left and right are not equal; otherwise, false. + public static bool operator !=(HslColor left, HslColor right) + { + return !(left == right); + } + + /// + /// Explicit conversion from an to a . + /// + /// The to convert. + public static explicit operator Color(HslColor hslColor) + { + return hslColor.ToRgb(); + } + } +} diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index c58db9df00..6a27c39c9b 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -338,10 +338,10 @@ namespace Avalonia.Media /// /// Converts the given HSVA color component values to it's RGB color equivalent. /// - /// The hue component in the HSV color model in the range from 0..360. - /// The saturation component in the HSV color model in the range from 0..1. - /// The value component in the HSV color model in the range from 0..1. - /// The alpha component in the range from 0..1. + /// The Hue component in the HSV color model in the range from 0..360. + /// The Saturation component in the HSV color model in the range from 0..1. + /// The Value component in the HSV color model in the range from 0..1. + /// The Alpha component in the range from 0..1. /// A new RGB equivalent to the given HSVA values. public static Color ToRgb( double hue, @@ -467,10 +467,10 @@ namespace Avalonia.Media } return Color.FromArgb( - (byte)Math.Round(alpha * 255), - (byte)Math.Round(r * 255), - (byte)Math.Round(g * 255), - (byte)Math.Round(b * 255)); + (byte)(alpha * 255), + (byte)(r * 255), + (byte)(g * 255), + (byte)(b * 255)); } /// From 1d0668a1846266d9cfb75516b7379852789fcb9f Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 9 Apr 2022 00:07:58 -0400 Subject: [PATCH 05/22] Move RgbToHsv conversion into Color All color types will ONLY have .ToModel() conversion methods. .FromModel() conversion will not be provided. This unifies the API and allows easily extending with more color models in the future without breaking things. Each type is responsible to convert itself to other supported types -- direction is one way in the API. --- src/Avalonia.Visuals/Media/Color.cs | 133 ++++++++++++++++++++++++- src/Avalonia.Visuals/Media/HsvColor.cs | 132 +----------------------- 2 files changed, 136 insertions(+), 129 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 84fde9bfc2..107bfc7a8e 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -1,3 +1,10 @@ +// Color conversion portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// and the Windows Community Toolkit project. +// (https://github.com/CommunityToolkit/WindowsCommunityToolkit) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. + using System; using System.Globalization; #if !BUILDTASK @@ -280,7 +287,7 @@ namespace Avalonia.Media { // Use the by-component conversion method directly for performance // Don't use the HsvColor(Color) constructor to avoid an extra HsvColor - return HsvColor.FromRgb(R, G, B, A); + return Color.ToHsv(R, G, B, A); } /// @@ -308,6 +315,130 @@ namespace Avalonia.Media } } + /// + /// Converts the given RGB color to it's HSV color equivalent. + /// + /// The color in the RGB color model. + /// A new equivalent to the given RGBA values. + public static HsvColor ToHsv(Color color) + { + return Color.ToHsv(color.R, color.G, color.B, color.A); + } + + /// + /// Converts the given RGBA color component values to its HSV color equivalent. + /// + /// The red component in the RGB color model. + /// The green component in the RGB color model. + /// The blue component in the RGB color model. + /// The alpha component. + /// A new equivalent to the given RGBA values. + public static HsvColor ToHsv( + byte red, + byte green, + byte blue, + byte alpha = 0xFF) + { + // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) + // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp + // This was used because it is the best documented and likely most optimized for performance + // Alpha support was added + + // Normalize RGBA components into the 0..1 range used by this algorithm + double r = red / 255.0; + double g = green / 255.0; + double b = blue / 255.0; + double a = alpha / 255.0; + + double hue; + double saturation; + double value; + + double max = r >= g ? (r >= b ? r : b) : (g >= b ? g : b); + double min = r <= g ? (r <= b ? r : b) : (g <= b ? g : b); + + // The value, a number between 0 and 1, is the largest of R, G, and B (divided by 255). + // Conceptually speaking, it represents how much color is present. + // If at least one of R, G, B is 255, then there exists as much color as there can be. + // If RGB = (0, 0, 0), then there exists no color at all - a value of zero corresponds + // to black (i.e., the absence of any color). + value = max; + + // The "chroma" of the color is a value directly proportional to the extent to which + // the color diverges from greyscale. If, for example, we have RGB = (255, 255, 0), + // then the chroma is maximized - this is a pure yellow, no gray of any kind. + // On the other hand, if we have RGB = (128, 128, 128), then the chroma being zero + // implies that this color is pure greyscale, with no actual hue to be found. + var chroma = max - min; + + // If the chrome is zero, then hue is technically undefined - a greyscale color + // has no hue. For the sake of convenience, we'll just set hue to zero, since + // it will be unused in this circumstance. Since the color is purely gray, + // saturation is also equal to zero - you can think of saturation as basically + // a measure of hue intensity, such that no hue at all corresponds to a + // nonexistent intensity. + if (chroma == 0) + { + hue = 0.0; + saturation = 0.0; + } + else + { + // In this block, hue is properly defined, so we'll extract both hue + // and saturation information from the RGB color. + + // Hue can be thought of as a cyclical thing, between 0 degrees and 360 degrees. + // A hue of 0 degrees is red; 120 degrees is green; 240 degrees is blue; and 360 is back to red. + // Every other hue is somewhere between either red and green, green and blue, and blue and red, + // so every other hue can be thought of as an angle on this color wheel. + // These if/else statements determines where on this color wheel our color lies. + if (r == max) + { + // If the red channel is the most pronounced channel, then we exist + // somewhere between (-60, 60) on the color wheel - i.e., the section around 0 degrees + // where red dominates. We figure out where in that section we are exactly + // by considering whether the green or the blue channel is greater - by subtracting green from blue, + // then if green is greater, we'll nudge ourselves closer to 60, whereas if blue is greater, then + // we'll nudge ourselves closer to -60. We then divide by chroma (which will actually make the result larger, + // since chroma is a value between 0 and 1) to normalize the value to ensure that we get the right hue + // even if we're very close to greyscale. + hue = 60 * (g - b) / chroma; + } + else if (g == max) + { + // We do the exact same for the case where the green channel is the most pronounced channel, + // only this time we want to see if we should tilt towards the blue direction or the red direction. + // We add 120 to center our value in the green third of the color wheel. + hue = 120 + (60 * (b - r) / chroma); + } + else // blue == max + { + // And we also do the exact same for the case where the blue channel is the most pronounced channel, + // only this time we want to see if we should tilt towards the red direction or the green direction. + // We add 240 to center our value in the blue third of the color wheel. + hue = 240 + (60 * (r - g) / chroma); + } + + // Since we want to work within the range [0, 360), we'll add 360 to any value less than zero - + // this will bump red values from within -60 to -1 to 300 to 359. The hue is the same at both values. + if (hue < 0.0) + { + hue += 360.0; + } + + // The saturation, our final HSV axis, can be thought of as a value between 0 and 1 indicating how intense our color is. + // To find it, we divide the chroma - the distance between the minimum and the maximum RGB channels - by the maximum channel (i.e., the value). + // This effectively normalizes the chroma - if the maximum is 0.5 and the minimum is 0, the saturation will be (0.5 - 0) / 0.5 = 1, + // meaning that although this color is not as bright as it can be, the dark color is as intense as it possibly could be. + // If, on the other hand, the maximum is 0.5 and the minimum is 0.25, then the saturation will be (0.5 - 0.25) / 0.5 = 0.5, + // meaning that this color is partially washed out. + // A saturation value of 0 corresponds to a greyscale color, one in which the color is *completely* washed out and there is no actual hue. + saturation = chroma / value; + } + + return new HsvColor(a, hue, saturation, value, false); + } + /// /// Indicates whether the values of two specified objects are equal. /// diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 6a27c39c9b..fcfd6a7040 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -1,6 +1,6 @@ -// Color conversion portions of this source file are adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// +// Color conversion portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; @@ -89,7 +89,7 @@ namespace Avalonia.Media /// The RGB color to convert to HSV. public HsvColor(Color color) { - var hsv = HsvColor.FromRgb(color); + var hsv = Color.ToHsv(color); A = hsv.A; H = hsv.H; @@ -473,130 +473,6 @@ namespace Avalonia.Media (byte)(b * 255)); } - /// - /// Converts the given RGB color to it's HSV color equivalent. - /// - /// The color in the RGB color model. - /// A new equivalent to the given RGBA values. - public static HsvColor FromRgb(Color color) - { - return HsvColor.FromRgb(color.R, color.G, color.B, color.A); - } - - /// - /// Converts the given RGBA color component values to its HSV color equivalent. - /// - /// The red component in the RGB color model. - /// The green component in the RGB color model. - /// The blue component in the RGB color model. - /// The alpha component. - /// A new equivalent to the given RGBA values. - public static HsvColor FromRgb( - byte red, - byte green, - byte blue, - byte alpha = 0xFF) - { - // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) - // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp - // This was used because it is the best documented and likely most optimized for performance - // Alpha support was added - - // Normalize RGBA components into the 0..1 range used by this algorithm - double r = red / 255.0; - double g = green / 255.0; - double b = blue / 255.0; - double a = alpha / 255.0; - - double hue; - double saturation; - double value; - - double max = r >= g ? (r >= b ? r : b) : (g >= b ? g : b); - double min = r <= g ? (r <= b ? r : b) : (g <= b ? g : b); - - // The value, a number between 0 and 1, is the largest of R, G, and B (divided by 255). - // Conceptually speaking, it represents how much color is present. - // If at least one of R, G, B is 255, then there exists as much color as there can be. - // If RGB = (0, 0, 0), then there exists no color at all - a value of zero corresponds - // to black (i.e., the absence of any color). - value = max; - - // The "chroma" of the color is a value directly proportional to the extent to which - // the color diverges from greyscale. If, for example, we have RGB = (255, 255, 0), - // then the chroma is maximized - this is a pure yellow, no gray of any kind. - // On the other hand, if we have RGB = (128, 128, 128), then the chroma being zero - // implies that this color is pure greyscale, with no actual hue to be found. - var chroma = max - min; - - // If the chrome is zero, then hue is technically undefined - a greyscale color - // has no hue. For the sake of convenience, we'll just set hue to zero, since - // it will be unused in this circumstance. Since the color is purely gray, - // saturation is also equal to zero - you can think of saturation as basically - // a measure of hue intensity, such that no hue at all corresponds to a - // nonexistent intensity. - if (chroma == 0) - { - hue = 0.0; - saturation = 0.0; - } - else - { - // In this block, hue is properly defined, so we'll extract both hue - // and saturation information from the RGB color. - - // Hue can be thought of as a cyclical thing, between 0 degrees and 360 degrees. - // A hue of 0 degrees is red; 120 degrees is green; 240 degrees is blue; and 360 is back to red. - // Every other hue is somewhere between either red and green, green and blue, and blue and red, - // so every other hue can be thought of as an angle on this color wheel. - // These if/else statements determines where on this color wheel our color lies. - if (r == max) - { - // If the red channel is the most pronounced channel, then we exist - // somewhere between (-60, 60) on the color wheel - i.e., the section around 0 degrees - // where red dominates. We figure out where in that section we are exactly - // by considering whether the green or the blue channel is greater - by subtracting green from blue, - // then if green is greater, we'll nudge ourselves closer to 60, whereas if blue is greater, then - // we'll nudge ourselves closer to -60. We then divide by chroma (which will actually make the result larger, - // since chroma is a value between 0 and 1) to normalize the value to ensure that we get the right hue - // even if we're very close to greyscale. - hue = 60 * (g - b) / chroma; - } - else if (g == max) - { - // We do the exact same for the case where the green channel is the most pronounced channel, - // only this time we want to see if we should tilt towards the blue direction or the red direction. - // We add 120 to center our value in the green third of the color wheel. - hue = 120 + (60 * (b - r) / chroma); - } - else // blue == max - { - // And we also do the exact same for the case where the blue channel is the most pronounced channel, - // only this time we want to see if we should tilt towards the red direction or the green direction. - // We add 240 to center our value in the blue third of the color wheel. - hue = 240 + (60 * (r - g) / chroma); - } - - // Since we want to work within the range [0, 360), we'll add 360 to any value less than zero - - // this will bump red values from within -60 to -1 to 300 to 359. The hue is the same at both values. - if (hue < 0.0) - { - hue += 360.0; - } - - // The saturation, our final HSV axis, can be thought of as a value between 0 and 1 indicating how intense our color is. - // To find it, we divide the chroma - the distance between the minimum and the maximum RGB channels - by the maximum channel (i.e., the value). - // This effectively normalizes the chroma - if the maximum is 0.5 and the minimum is 0, the saturation will be (0.5 - 0) / 0.5 = 1, - // meaning that although this color is not as bright as it can be, the dark color is as intense as it possibly could be. - // If, on the other hand, the maximum is 0.5 and the minimum is 0.25, then the saturation will be (0.5 - 0.25) / 0.5 = 0.5, - // meaning that this color is partially washed out. - // A saturation value of 0 corresponds to a greyscale color, one in which the color is *completely* washed out and there is no actual hue. - saturation = chroma / value; - } - - return new HsvColor(a, hue, saturation, value, false); - } - /// /// Indicates whether the values of two specified objects are equal. /// From 1ea30b53ff31f866d2e1cd74f30dd8676ebd3a55 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 9 Apr 2022 00:43:34 -0400 Subject: [PATCH 06/22] Add Color.ToHsl() conversions --- src/Avalonia.Visuals/Media/Color.cs | 155 ++++++++++++++++++++++--- src/Avalonia.Visuals/Media/HslColor.cs | 18 ++- src/Avalonia.Visuals/Media/HsvColor.cs | 4 +- 3 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 107bfc7a8e..da55147f8a 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -21,6 +21,8 @@ namespace Avalonia.Media #endif readonly struct Color : IEquatable { + private const double byteToDouble = 1.0 / 255; + static Color() { #if !BUILDTASK @@ -279,13 +281,22 @@ namespace Avalonia.Media return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B; } + /// + /// Returns the HSL color model equivalent of this RGB color. + /// + /// The HSL equivalent color. + public HslColor ToHsl() + { + // Don't use the HslColor(Color) constructor to avoid an extra HslColor + return Color.ToHsl(R, G, B, A); + } + /// /// Returns the HSV color model equivalent of this RGB color. /// /// The HSV equivalent color. public HsvColor ToHsv() { - // Use the by-component conversion method directly for performance // Don't use the HsvColor(Color) constructor to avoid an extra HsvColor return Color.ToHsv(R, G, B, A); } @@ -316,40 +327,154 @@ namespace Avalonia.Media } /// - /// Converts the given RGB color to it's HSV color equivalent. + /// Converts the given RGB color to its HSL color equivalent. + /// + /// The color in the RGB color model. + /// A new equivalent to the given RGBA values. + public static HslColor ToHsl(Color color) + { + // Normalize RGBA components into the 0..1 range + return Color.ToHsl( + (byteToDouble * color.R), + (byteToDouble * color.G), + (byteToDouble * color.B), + (byteToDouble * color.A)); + } + + /// + /// Converts the given RGBA color component values to their HSL color equivalent. + /// + /// The Red component in the RGB color model. + /// The Green component in the RGB color model. + /// The Blue component in the RGB color model. + /// The Alpha component. + /// A new equivalent to the given RGBA values. + public static HslColor ToHsl( + byte red, + byte green, + byte blue, + byte alpha = 0xFF) + { + // Normalize RGBA components into the 0..1 range + return Color.ToHsl( + (byteToDouble * red), + (byteToDouble * green), + (byteToDouble * blue), + (byteToDouble * alpha)); + } + + /// + /// Converts the given RGBA color component values to their HSL color equivalent. + /// + /// + /// Warning: No bounds checks or clamping is done on the input component values. + /// This method is for internal-use only and the caller must ensure bounds. + /// + /// The Red component in the RGB color model within the range 0..1. + /// The Green component in the RGB color model within the range 0..1. + /// The Blue component in the RGB color model within the range 0..1. + /// The Alpha component in the RGB color model within the range 0..1. + /// A new equivalent to the given RGBA values. + internal static HslColor ToHsl( + double r, + double g, + double b, + double a = 1.0) + { + // Note: Conversion code is originally based on ColorHelper in the Windows Community Toolkit (licensed MIT) + // https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp/Helpers/ColorHelper.cs + // It has been modified. + + var max = Math.Max(Math.Max(r, g), b); + var min = Math.Min(Math.Min(r, g), b); + var chroma = max - min; + double h1; + + if (chroma == 0) + { + h1 = 0; + } + else if (max == r) + { + // The % operator doesn't do proper modulo on negative + // numbers, so we'll add 6 before using it + h1 = (((g - b) / chroma) + 6) % 6; + } + else if (max == g) + { + h1 = 2 + ((b - r) / chroma); + } + else + { + h1 = 4 + ((r - g) / chroma); + } + + double lightness = 0.5 * (max + min); + double saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs((2 * lightness) - 1)); + + return new HslColor(a, 60 * h1, saturation, lightness, clampValues: false); + } + + /// + /// Converts the given RGB color to its HSV color equivalent. /// /// The color in the RGB color model. /// A new equivalent to the given RGBA values. public static HsvColor ToHsv(Color color) { - return Color.ToHsv(color.R, color.G, color.B, color.A); + // Normalize RGBA components into the 0..1 range + return Color.ToHsv( + (byteToDouble * color.R), + (byteToDouble * color.G), + (byteToDouble * color.B), + (byteToDouble * color.A)); } /// - /// Converts the given RGBA color component values to its HSV color equivalent. + /// Converts the given RGBA color component values to their HSV color equivalent. /// - /// The red component in the RGB color model. - /// The green component in the RGB color model. - /// The blue component in the RGB color model. - /// The alpha component. + /// The Red component in the RGB color model. + /// The Green component in the RGB color model. + /// The Blue component in the RGB color model. + /// The Alpha component. /// A new equivalent to the given RGBA values. public static HsvColor ToHsv( byte red, byte green, byte blue, byte alpha = 0xFF) + { + // Normalize RGBA components into the 0..1 range + return Color.ToHsv( + (byteToDouble * red), + (byteToDouble * green), + (byteToDouble * blue), + (byteToDouble * alpha)); + } + + /// + /// Converts the given RGBA color component values to their HSV color equivalent. + /// + /// + /// Warning: No bounds checks or clamping is done on the input component values. + /// This method is for internal-use only and the caller must ensure bounds. + /// + /// The Red component in the RGB color model within the range 0..1. + /// The Green component in the RGB color model within the range 0..1. + /// The Blue component in the RGB color model within the range 0..1. + /// The Alpha component in the RGB color model within the range 0..1. + /// A new equivalent to the given RGBA values. + internal static HsvColor ToHsv( + double r, + double g, + double b, + double a = 1.0) { // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp // This was used because it is the best documented and likely most optimized for performance // Alpha support was added - // Normalize RGBA components into the 0..1 range used by this algorithm - double r = red / 255.0; - double g = green / 255.0; - double b = blue / 255.0; - double a = alpha / 255.0; - double hue; double saturation; double value; @@ -436,7 +561,7 @@ namespace Avalonia.Media saturation = chroma / value; } - return new HsvColor(a, hue, saturation, value, false); + return new HsvColor(a, hue, saturation, value, clampValues: false); } /// diff --git a/src/Avalonia.Visuals/Media/HslColor.cs b/src/Avalonia.Visuals/Media/HslColor.cs index 21ab669a05..af4a9e20f4 100644 --- a/src/Avalonia.Visuals/Media/HslColor.cs +++ b/src/Avalonia.Visuals/Media/HslColor.cs @@ -83,6 +83,20 @@ namespace Avalonia.Media } } + /// + /// Initializes a new instance of the struct. + /// + /// The RGB color to convert to HSL. + public HslColor(Color color) + { + var hsl = Color.ToHsl(color); + + A = hsl.A; + H = hsl.H; + S = hsl.S; + L = hsl.L; + } + /// /// Gets the Alpha (transparency) component in the range from 0..1. /// @@ -189,7 +203,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSL color to it's RGB color equivalent. + /// Converts the given HSL color to its RGB color equivalent. /// /// The color in the HSL color model. /// A new RGB equivalent to the given HSLA values. @@ -199,7 +213,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSLA color component values to it's RGB color equivalent. + /// Converts the given HSLA color component values to their RGB color equivalent. /// /// The Hue component in the HSL color model in the range from 0..360. /// The Saturation component in the HSL color model in the range from 0..1. diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index fcfd6a7040..42333f7049 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -326,7 +326,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSV color to it's RGB color equivalent. + /// Converts the given HSV color to its RGB color equivalent. /// /// The color in the HSV color model. /// A new RGB equivalent to the given HSVA values. @@ -336,7 +336,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSVA color component values to it's RGB color equivalent. + /// Converts the given HSVA color component values to their RGB color equivalent. /// /// The Hue component in the HSV color model in the range from 0..360. /// The Saturation component in the HSV color model in the range from 0..1. From 6846e317bee51be169f6cf52f39877755e555edf Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 9 Apr 2022 00:45:06 -0400 Subject: [PATCH 07/22] Add constructor comments --- src/Avalonia.Visuals/Media/Color.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index da55147f8a..b1640b9953 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -50,6 +50,13 @@ namespace Avalonia.Media /// public byte B { get; } + /// + /// Initializes a new instance of the struct. + /// + /// The alpha component. + /// The red component. + /// The green component. + /// The blue component. public Color(byte a, byte r, byte g, byte b) { A = a; From af7ad6071693437347a4154240907cb04652492d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 9 Apr 2022 15:24:36 +0200 Subject: [PATCH 08/22] Fix NativeMenuBar exception. The `x:DataType="NativeMenuItem"` directive is incorrect as the items can also be `NativeMenuItemSeparator`s which don't have the bound properties. Turn off compiled bindings here for now. Fixes #7780 --- src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 243095c004..7860e08ef5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -12,7 +12,7 @@ IsVisible="{Binding $parent[TopLevel].(NativeMenu.IsNativeMenuExported), Converter={StaticResource AvaloniaThemesDefaultNativeMenuBarInverseBooleanValueConverter}}" Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}"> - diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml index 0d1dd03c6e..e8e2df03b4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml @@ -61,17 +61,17 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index a861699675..11d6b9fdfc 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -43,7 +43,7 @@ @@ -165,7 +165,7 @@ Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" - TextElement.Foreground="{TemplateBinding Foreground}" + Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" CornerRadius="{TemplateBinding CornerRadius}"/> @@ -212,7 +212,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 24bdbca740..33d502772e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -110,7 +110,7 @@ BorderThickness="0" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" - TextElement.Foreground="{DynamicResource ExpanderForeground}" /> + Foreground="{DynamicResource ExpanderForeground}" /> @@ -49,7 +49,7 @@ @@ -57,7 +57,7 @@ @@ -65,7 +65,7 @@ @@ -73,7 +73,7 @@ @@ -81,7 +81,7 @@ @@ -89,6 +89,6 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 09f11d9c11..33cf6bfdde 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -218,7 +218,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml index fd3e3b0ed6..cd2c02c567 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml @@ -208,7 +208,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index 059e041e25..f86b67bb6c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -107,7 +107,7 @@ @@ -116,7 +116,7 @@ @@ -125,7 +125,7 @@ @@ -134,7 +134,7 @@ @@ -143,7 +143,7 @@ @@ -152,7 +152,7 @@ @@ -161,7 +161,7 @@ From 8997c9d9dfb72478523d2d1da3986795901a529d Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Sun, 10 Apr 2022 18:31:20 -0400 Subject: [PATCH 21/22] Update ApiCompat --- src/Avalonia.Controls/ApiCompatBaseline.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 12afe71f77..fe3ac31734 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -36,6 +36,11 @@ MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.ForegroundProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontSizeProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextAlignmentProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextTrimmingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextWrappingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.LineHeightProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.MaxLinesProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.FontFamily Avalonia.Controls.TextBlock.GetFontFamily(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Double Avalonia.Controls.TextBlock.GetFontSize(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.FontStyle Avalonia.Controls.TextBlock.GetFontStyle(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. @@ -89,4 +94,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 90 +Total Issues: 95 From ff3fe479b1ad0dcef536c5d21cbbd2b7e399cb1c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 11 Apr 2022 13:53:08 +0200 Subject: [PATCH 22/22] Fix ControlCatalog.Web build --- .../ControlCatalog.Web.csproj | 42 +++++++++++++------ samples/ControlCatalog.Web/LinkerConfig.xml | 28 ------------- 2 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 samples/ControlCatalog.Web/LinkerConfig.xml diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj index 520bbdf32b..b2c9ec72eb 100644 --- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj +++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj @@ -1,15 +1,14 @@  net6.0 - false enable - True + + true + 16777216 + false + false - - - - false @@ -23,19 +22,36 @@ -O3 -O3 false + false + false + false + false + false + true + false + true + true + true + link + true - - + + - - - - - + + + + + + + + + + diff --git a/samples/ControlCatalog.Web/LinkerConfig.xml b/samples/ControlCatalog.Web/LinkerConfig.xml deleted file mode 100644 index 5839a0fe03..0000000000 --- a/samples/ControlCatalog.Web/LinkerConfig.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - -