diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Visuals/Media/DashStyle.cs index 7784c73736..bd81cb1d03 100644 --- a/src/Avalonia.Visuals/Media/DashStyle.cs +++ b/src/Avalonia.Visuals/Media/DashStyle.cs @@ -9,7 +9,7 @@ namespace Avalonia.Media /// /// Represents the sequence of dashes and gaps that will be applied by a . /// - public class DashStyle : Animatable, IDashStyle, IAffectsRender + public class DashStyle : Animatable, IDashStyle, IAffectsRender, IEquatable { /// /// Defines the property. @@ -105,10 +105,64 @@ namespace Avalonia.Media /// public event EventHandler Invalidated; + /// + public override bool Equals(object obj) => DashEquals(this, obj as IDashStyle); + + /// + public bool Equals(IDashStyle other) => DashEquals(this, other); + + /// + public override int GetHashCode() => GetHashCode(this); + /// /// Returns an immutable clone of the . /// /// public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); + + internal static bool DashEquals(IDashStyle a, IDashStyle b) + { + if (ReferenceEquals(a, b)) + { + return true; + } + else if ((a is null && !(b is null)) || (b is null && !(a is null))) + { + return false; + } + + if (a.Offset != b.Offset) + { + return false; + } + + if (ReferenceEquals(a.Dashes, b.Dashes)) + { + return true; + } + + if ((a.Dashes is null && !(b.Dashes is null)) || (b.Dashes is null && !(a.Dashes is null))) + { + return false; + } + + return a.Dashes.SequenceEqual(b.Dashes); + } + + internal static int GetHashCode(IDashStyle style) + { + var hashCode = 717868523; + hashCode = hashCode * -1521134295 + style.Offset.GetHashCode(); + + if (style.Dashes != null) + { + foreach (var i in style.Dashes) + { + hashCode = hashCode * -1521134295 + i.GetHashCode(); + } + } + + return hashCode; + } } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs index a40682babd..65e27bf9b5 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media.Immutable /// Represents the sequence of dashes and gaps that will be applied by an /// . /// - public class ImmutableDashStyle : IDashStyle + public class ImmutableDashStyle : IDashStyle, IEquatable { /// /// Initializes a new instance of the class. @@ -26,5 +26,15 @@ namespace Avalonia.Media.Immutable /// public double Offset { get; } + + /// + public override bool Equals(object obj) => DashStyle.DashEquals(this, obj as IDashStyle); + + /// + public bool Equals(IDashStyle other) => DashStyle.DashEquals(this, other); + + /// + public override int GetHashCode() => DashStyle.GetHashCode(this); + } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs index 70b4281083..d5601c7497 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs @@ -65,5 +65,27 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.True(Equals(target1, target2)); } + + [Fact] + public void Equality_Is_Implemented_Between_Mutable_And_Immutable_DashStyles() + { + var brush = new SolidColorBrush(Colors.Red); + var target1 = new Pen( + brush: brush, + thickness: 2, + dashStyle: new DashStyle(new[] { 0.1, 0.2 }, 5), + lineCap: PenLineCap.Round, + lineJoin: PenLineJoin.Round, + miterLimit: 21); + var target2 = new ImmutablePen( + brush: brush, + thickness: 2, + dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5), + lineCap: PenLineCap.Round, + lineJoin: PenLineJoin.Round, + miterLimit: 21); + + Assert.True(Equals(target1, target2)); + } } }