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);
}