using System; using System.Collections.Generic; using System.Collections.Specialized; using Avalonia.Animation; using Avalonia.Collections; using Avalonia.Media.Immutable; using Avalonia.Reactive; #nullable enable namespace Avalonia.Media { /// /// Represents the sequence of dashes and gaps that will be applied by a . /// public class DashStyle : Animatable, IDashStyle, IAffectsRender { /// /// Defines the property. /// public static readonly StyledProperty?> DashesProperty = AvaloniaProperty.Register?>(nameof(Dashes)); /// /// Defines the property. /// public static readonly StyledProperty OffsetProperty = AvaloniaProperty.Register(nameof(Offset)); private static ImmutableDashStyle? s_dash; private static ImmutableDashStyle? s_dot; private static ImmutableDashStyle? s_dashDot; private static ImmutableDashStyle? s_dashDotDot; /// /// Initializes a new instance of the class. /// public DashStyle() { } /// /// Initializes a new instance of the class. /// /// The dashes collection. /// The dash sequence offset. public DashStyle(IEnumerable? dashes, double offset) { Dashes = (dashes as AvaloniaList) ?? new AvaloniaList(dashes ?? Array.Empty()); Offset = offset; } static DashStyle() { var invalidateObserver = new AnonymousObserver( static e => ((DashStyle)e.Sender).Invalidated?.Invoke(e.Sender, EventArgs.Empty)); DashesProperty.Changed.Subscribe(invalidateObserver); OffsetProperty.Changed.Subscribe(invalidateObserver); } /// /// Represents a dashed . /// public static IDashStyle Dash => s_dash ??= new ImmutableDashStyle(new double[] { 2, 2 }, 1); /// /// Represents a dotted . /// public static IDashStyle Dot => s_dot ??= new ImmutableDashStyle(new double[] { 0, 2 }, 0); /// /// Represents a dashed dotted . /// public static IDashStyle DashDot => s_dashDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1); /// /// Represents a dashed double dotted . /// public static IDashStyle DashDotDot => s_dashDotDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1); /// /// Gets or sets the length of alternating dashes and gaps. /// public AvaloniaList? Dashes { get => GetValue(DashesProperty); set => SetValue(DashesProperty, value); } /// /// Gets or sets how far in the dash sequence the stroke will start. /// public double Offset { get => GetValue(OffsetProperty); set => SetValue(OffsetProperty, value); } IReadOnlyList? IDashStyle.Dashes => Dashes; /// /// Raised when the dash style changes. /// public event EventHandler? Invalidated; /// /// Returns an immutable clone of the . /// /// public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == DashesProperty) { var (oldValue, newValue) = change.GetOldAndNewValue>(); if (oldValue is object) { oldValue.CollectionChanged -= DashesChanged; } if (newValue is object) { newValue.CollectionChanged += DashesChanged; } } } private void DashesChanged(object? sender, NotifyCollectionChangedEventArgs e) { Invalidated?.Invoke(this, e); } } }