From 8a444ef3f0b43fe753e5ea0f998773122bfa1faf Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 14 Sep 2025 18:59:18 -0400 Subject: [PATCH] Add Boxshadow(s) Documentation Comments (#19644) * Fix formatting errors/warnings in BoxShadow * Fix formatting errors/warnings in BoxShadows * Add documentation comments to BoxShadow * Add documentation comments to BoxShadows --- src/Avalonia.Base/Media/BoxShadow.cs | 142 ++++++++++++++++++++++++-- src/Avalonia.Base/Media/BoxShadows.cs | 118 +++++++++++++++++++-- 2 files changed, 241 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Base/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs index 4b04f84938..7399bf121b 100644 --- a/src/Avalonia.Base/Media/BoxShadow.cs +++ b/src/Avalonia.Base/Media/BoxShadow.cs @@ -6,25 +6,90 @@ using Avalonia.Utilities; namespace Avalonia.Media { + /// + /// Represents a box shadow which can be attached to an element or control. + /// public struct BoxShadow { + private readonly static char[] s_Separator = new char[] { ' ', '\t' }; + + /// + /// Gets or sets the horizontal offset (distance) of the shadow. + /// + /// + /// Positive values place the shadow to the right of the element while + /// negative values place the shadow to the left. + /// public double OffsetX { get; set; } + + /// + /// Gets or sets the vertical offset (distance) of the shadow. + /// + /// + /// Positive values place the shadow below the element while + /// negative values place the shadow above. + /// public double OffsetY { get; set; } + + /// + /// Gets or sets the blur radius. + /// This is used to control the amount of blurring. + /// + /// + /// The larger this value, the bigger the blur effect, so the shadow becomes larger and more transparent. + /// Negative values are not allowed. If not specified, the default (zero) is used and the shadow edge is sharp. + /// public double Blur { get; set; } + + /// + /// Gets or sets the spread radius. + /// This is used to control the overall size of the shadow. + /// + /// + /// Positive values will cause the shadow to expand and grow larger, negative values will cause the shadow to shrink. + /// If not specified, the default (zero) is used and the shadow will be the same size as the element. + /// public double Spread { get; set; } + + /// + /// Gets or sets the color of the shadow. + /// public Color Color { get; set; } + + /// + /// Gets or sets a value indicating whether the shadow is inset and drawn within the element rather than outside of it. + /// + /// + /// Inset changes the shadow to inside the element (as if the content was depressed inside the box). + /// If false (the default), the shadow is assumed to be a drop shadow (as if the box were raised above the content). + ///

+ /// Inset shadows are drawn inside the element, above the background (even when it's transparent), but below any content. + ///
public bool IsInset { get; set; } + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the other parameter; otherwise, false. + /// public bool Equals(in BoxShadow other) { - return OffsetX.Equals(other.OffsetX) && OffsetY.Equals(other.OffsetY) && Blur.Equals(other.Blur) && Spread.Equals(other.Spread) && Color.Equals(other.Color); + return OffsetX.Equals(other.OffsetX) + && OffsetY.Equals(other.OffsetY) + && Blur.Equals(other.Blur) + && Spread.Equals(other.Spread) + && Color.Equals(other.Color); } + /// public override bool Equals(object? obj) { return obj is BoxShadow other && Equals(other); } + /// public override int GetHashCode() { unchecked @@ -38,8 +103,6 @@ namespace Avalonia.Media } } - private readonly static char[] s_Separator = new char[] { ' ', '\t' }; - struct ArrayReader { private int _index; @@ -55,20 +118,28 @@ namespace Avalonia.Media { s = null; if (_index >= _arr.Length) + { return false; + } + s = _arr[_index]; _index++; + return true; } public string ReadString() { - if(!TryReadString(out var rv)) + if (!TryReadString(out var rv)) + { throw new FormatException(); + } + return rv; } } + /// public override string ToString() { var sb = StringBuilderCache.Acquire(); @@ -106,20 +177,45 @@ namespace Avalonia.Media Color.ToString(sb); } + /// + /// Parses a string. + /// + /// + /// A box shadow may be specified in multiple formats with separate components: + /// + /// Two, three, or four length values. + /// A color value. + /// An optional inset keyword. + /// + /// If only two length values are given they will be interpreted as and . + /// If a third value is given, it is interpreted as a , and if a fourth value is given, + /// it is interpreted as . + /// + /// The input string to parse. + /// A new public static unsafe BoxShadow Parse(string s) { - if(s == null) + if (s == null) + { throw new ArgumentNullException(); + } + if (s.Length == 0) + { throw new FormatException(); + } var p = s.Split(s_Separator, StringSplitOptions.RemoveEmptyEntries); if (p.Length == 1 && p[0] == "none") + { return default; - + } + if (p.Length < 3 || p.Length > 6) + { throw new FormatException(); - + } + bool inset = false; var tokenizer = new ArrayReader(p); @@ -135,16 +231,20 @@ namespace Avalonia.Media var offsetY = double.Parse(tokenizer.ReadString(), CultureInfo.InvariantCulture); double blur = 0; double spread = 0; - tokenizer.TryReadString(out var token3); tokenizer.TryReadString(out var token4); tokenizer.TryReadString(out var token5); - if (token4 != null) + if (token4 != null) + { blur = double.Parse(token3!, CultureInfo.InvariantCulture); + } + if (token5 != null) + { spread = double.Parse(token4!, CultureInfo.InvariantCulture); + } var color = Color.Parse(token5 ?? token4 ?? token3!); return new BoxShadow @@ -158,12 +258,36 @@ namespace Avalonia.Media }; } + /// + /// Transforms the specified bounding rectangle to account for the shadow's offset, spread, and blur. + /// + /// The original bounding to transform. + /// + /// A new that includes the shadow's offset, spread, and blur if the shadow is not inset; + /// otherwise, the original rectangle. + /// public Rect TransformBounds(in Rect rect) => IsInset ? rect : rect.Translate(new Vector(OffsetX, OffsetY)).Inflate(Spread + Blur); + /// + /// Determines whether two values are equal. + /// + /// The first to compare. + /// The second to compare. + /// + /// true if the two values are equal; otherwise, false. + /// public static bool operator ==(BoxShadow left, BoxShadow right) => left.Equals(right); + /// + /// Determines whether two values are not equal. + /// + /// The first to compare. + /// The second to compare. + /// + /// true if the two values are not equal; otherwise, false. + /// public static bool operator !=(BoxShadow left, BoxShadow right) => !(left == right); } diff --git a/src/Avalonia.Base/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs index 400aafa0df..02e9d4279b 100644 --- a/src/Avalonia.Base/Media/BoxShadows.cs +++ b/src/Avalonia.Base/Media/BoxShadows.cs @@ -1,16 +1,28 @@ using System; using System.ComponentModel; -using Avalonia.Animation.Animators; using Avalonia.Utilities; namespace Avalonia.Media { + /// + /// Represents a collection of s. + /// public struct BoxShadows { + private static readonly char[] s_Separators = new[] { ',' }; + private readonly BoxShadow _first; private readonly BoxShadow[]? _list; + + /// + /// Gets the number of s in the collection. + /// public int Count { get; } - + + /// + /// Initializes a new instance of the struct. + /// + /// The first to add to the collection. public BoxShadows(BoxShadow shadow) { _first = shadow; @@ -18,6 +30,11 @@ namespace Avalonia.Media Count = _first == default ? 0 : 1; } + /// + /// Initializes a new instance of the struct. + /// + /// The first to add to the collection. + /// All remaining s to add to the collection. public BoxShadows(BoxShadow first, BoxShadow[] rest) { _first = first; @@ -25,18 +42,33 @@ namespace Avalonia.Media Count = 1 + (rest?.Length ?? 0); } - public BoxShadow this[int c] + /// + /// Gets the at the specified index. + /// + /// The index of the to return. + /// The at the specified index. + /// + /// Thrown when index less than 0 or index greater than or equal to . + /// + public BoxShadow this[int index] { get { - if (c< 0 || c >= Count) + if (index < 0 || index >= Count) + { throw new IndexOutOfRangeException(); - if (c == 0) + } + + if (index == 0) + { return _first; - return _list![c - 1]; + } + + return _list![index - 1]; } } + /// public override string ToString() { if (Count == 0) @@ -81,7 +113,11 @@ namespace Avalonia.Media [EditorBrowsable(EditorBrowsableState.Never)] public BoxShadowsEnumerator GetEnumerator() => new BoxShadowsEnumerator(this); - private static readonly char[] s_Separators = new[] { ',' }; + /// + /// Parses a string representing one or more s. + /// + /// The input string to parse. + /// A new collection. public static BoxShadows Parse(string s) { var sp = s.Split(s_Separators, StringSplitOptions.RemoveEmptyEntries); @@ -89,66 +125,128 @@ namespace Avalonia.Media || (sp.Length == 1 && (string.IsNullOrWhiteSpace(sp[0]) || sp[0] == "none"))) + { return new BoxShadows(); + } var first = BoxShadow.Parse(sp[0]); if (sp.Length == 1) + { return new BoxShadows(first); + } var rest = new BoxShadow[sp.Length - 1]; for (var c = 0; c < rest.Length; c++) + { rest[c] = BoxShadow.Parse(sp[c + 1]); + } + return new BoxShadows(first, rest); } + /// + /// Transforms the specified bounding rectangle to account for all shadow's offset, spread, and blur. + /// + /// The original bounding to transform. + /// + /// A new that includes all shadow's offset, spread, and blur in the collection. + /// public Rect TransformBounds(in Rect rect) { var final = rect; foreach (var shadow in this) + { final = final.Union(shadow.TransformBounds(rect)); + } + return final; } - + + /// + /// Gets a value indicating whether any in the collection has + /// set to true. + /// public bool HasInsetShadows { get { foreach(var boxShadow in this) + { if (boxShadow != default && boxShadow.IsInset) + { return true; + } + } + return false; } } - + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the other parameter; otherwise, false. + /// public bool Equals(BoxShadows other) { if (other.Count != Count) + { return false; - for(var c=0; c public override bool Equals(object? obj) { return obj is BoxShadows other && Equals(other); } + /// public override int GetHashCode() { unchecked { int hashCode = 0; foreach (var s in this) + { hashCode = (hashCode * 397) ^ s.GetHashCode(); + } + return hashCode; } } + /// + /// Determines whether two collections are equal. + /// + /// The first collection to compare. + /// The second collection to compare. + /// + /// true if the two collections are equal; otherwise, false. + /// public static bool operator ==(BoxShadows left, BoxShadows right) => left.Equals(right); + /// + /// Determines whether two collections are not equal. + /// + /// The first collection to compare. + /// The second collection to compare. + /// + /// true if the two collections are not equal; otherwise, false. + /// public static bool operator !=(BoxShadows left, BoxShadows right) => !(left == right); }