Browse Source

refactor(media): clarify color CSS formatters

Color formatting now exposes the same IFormattable implementation in the BuildTask compile path as in normal builds. Keeping the interface conditional made the public contract harder to reason about even though the formatting members are System-only APIs.

Remove the BuildTask guard from the formatting members, combine alpha and no-alpha CSS helpers behind includeAlpha parameters, and rename the helpers so future changes preserve the CSS-compliant output contract.
pull/20919/head
Nathan Nguyen 5 months ago
parent
commit
3d02651f68
  1. 55
      src/Avalonia.Base/Media/Color.cs
  2. 64
      src/Avalonia.Base/Media/HslColor.cs
  3. 64
      src/Avalonia.Base/Media/HsvColor.cs

55
src/Avalonia.Base/Media/Color.cs

@ -21,10 +21,7 @@ namespace Avalonia.Media
#if !BUILDTASK
public
#endif
readonly struct Color : IEquatable<Color>
#if !BUILDTASK
, IFormattable
#endif
readonly struct Color : IEquatable<Color>, IFormattable
{
private const double byteToDouble = 1.0 / 255;
@ -464,7 +461,6 @@ namespace Avalonia.Media
}
}
#if !BUILDTASK
/// <summary>
/// Returns a formatted string representation of the color.
/// </summary>
@ -501,10 +497,10 @@ namespace Avalonia.Media
"x" => $"#{R:X2}{G:X2}{B:X2}",
"H" => $"#{R:X2}{G:X2}{B:X2}{A:X2}",
"R" => string.Format(CultureInfo.InvariantCulture, "rgba({0}, {1}, {2}, {3:F2})", R, G, B, A * byteToDouble),
"r" => string.Format(CultureInfo.InvariantCulture, "rgb({0}, {1}, {2})", R, G, B),
"R%" => FormatRgbaPercent(),
"r%" => FormatRgbPercent(),
"R" => FormatRgbCss(),
"r" => FormatRgbCss(includeAlpha: false),
"R%" => FormatRgbPercentCss(),
"r%" => FormatRgbPercentCss(includeAlpha: false),
"L" or "l" or "L%" or "l%" => ToHsl().ToString(format, formatProvider),
"V" or "v" or "V%" or "v%" => ToHsv().ToString(format, formatProvider),
@ -513,23 +509,48 @@ namespace Avalonia.Media
};
}
private string FormatRgbaPercent()
/// <summary>
/// Formats the color as a CSS rgb() or rgba() string using absolute RGB components and fractional alpha.
/// </summary>
private string FormatRgbCss(bool includeAlpha = true)
{
int rPct = (int)Math.Round(R * byteToDouble * 100.0);
int gPct = (int)Math.Round(G * byteToDouble * 100.0);
int bPct = (int)Math.Round(B * byteToDouble * 100.0);
int aPct = (int)Math.Round(A * byteToDouble * 100.0);
return string.Format(CultureInfo.InvariantCulture, "rgba({0}%, {1}%, {2}%, {3}%)", rPct, gPct, bPct, aPct);
if (includeAlpha)
{
return string.Format(
CultureInfo.InvariantCulture,
"rgba({0}, {1}, {2}, {3:F2})",
R,
G,
B,
A * byteToDouble);
}
return string.Format(CultureInfo.InvariantCulture, "rgb({0}, {1}, {2})", R, G, B);
}
private string FormatRgbPercent()
/// <summary>
/// Formats the color as a CSS rgb() or rgba() string with all components as percentages.
/// </summary>
private string FormatRgbPercentCss(bool includeAlpha = true)
{
int rPct = (int)Math.Round(R * byteToDouble * 100.0);
int gPct = (int)Math.Round(G * byteToDouble * 100.0);
int bPct = (int)Math.Round(B * byteToDouble * 100.0);
if (includeAlpha)
{
int aPct = (int)Math.Round(A * byteToDouble * 100.0);
return string.Format(
CultureInfo.InvariantCulture,
"rgba({0}%, {1}%, {2}%, {3}%)",
rPct,
gPct,
bPct,
aPct);
}
return string.Format(CultureInfo.InvariantCulture, "rgb({0}%, {1}%, {2}%)", rPct, gPct, bPct);
}
#endif
/// <summary>
/// Returns the integer representation of the color.

64
src/Avalonia.Base/Media/HslColor.cs

@ -17,10 +17,7 @@ namespace Avalonia.Media
#if !BUILDTASK
public
#endif
readonly struct HslColor : IEquatable<HslColor>
#if !BUILDTASK
, IFormattable
#endif
readonly struct HslColor : IEquatable<HslColor>, IFormattable
{
/// <summary>
/// Initializes a new instance of the <see cref="HslColor"/> struct.
@ -180,7 +177,6 @@ namespace Avalonia.Media
return HslColor.ToHsv(H, S, L, A);
}
#if !BUILDTASK
/// <summary>
/// Returns a formatted string representation of the HSL color.
/// </summary>
@ -215,10 +211,10 @@ namespace Avalonia.Media
{
"X" or "x" or "H" or "R" or "r" or "R%" or "r%" => ToRgb().ToString(format, formatProvider),
"L" => FormatHsla(),
"l" => FormatHsl(),
"L%" => FormatHslaPercent(),
"l%" => FormatHslPercent(),
"L" => FormatHslCss(),
"l" => FormatHslCss(includeAlpha: false),
"L%" => FormatHslPercentCss(),
"l%" => FormatHslPercentCss(includeAlpha: false),
"V" or "v" or "V%" or "v%" => ToHsv().ToString(format, formatProvider),
@ -226,39 +222,53 @@ namespace Avalonia.Media
};
}
private string FormatHsla()
/// <summary>
/// Formats the color as a CSS hsl() or hsla() string using degrees, percent saturation/lightness,
/// and fractional alpha.
/// </summary>
private string FormatHslCss(bool includeAlpha = true)
{
int hDeg = (int)Math.Round(H);
int sPct = (int)Math.Round(S * 100.0);
int lPct = (int)Math.Round(L * 100.0);
return string.Format(CultureInfo.InvariantCulture, "hsla({0}, {1}%, {2}%, {3:F2})", hDeg, sPct, lPct, A);
}
private string FormatHsl()
{
int hDeg = (int)Math.Round(H);
int sPct = (int)Math.Round(S * 100.0);
int lPct = (int)Math.Round(L * 100.0);
if (includeAlpha)
{
return string.Format(
CultureInfo.InvariantCulture,
"hsla({0}, {1}%, {2}%, {3:F2})",
hDeg,
sPct,
lPct,
A);
}
return string.Format(CultureInfo.InvariantCulture, "hsl({0}, {1}%, {2}%)", hDeg, sPct, lPct);
}
private string FormatHslaPercent()
/// <summary>
/// Formats the color as a CSS hsl() or hsla() string with all components as percentages.
/// </summary>
private string FormatHslPercentCss(bool includeAlpha = true)
{
int hPct = (int)Math.Round(H / 360.0 * 100.0);
int sPct = (int)Math.Round(S * 100.0);
int lPct = (int)Math.Round(L * 100.0);
int aPct = (int)Math.Round(A * 100.0);
return string.Format(CultureInfo.InvariantCulture, "hsla({0}%, {1}%, {2}%, {3}%)", hPct, sPct, lPct, aPct);
}
private string FormatHslPercent()
{
int hPct = (int)Math.Round(H / 360.0 * 100.0);
int sPct = (int)Math.Round(S * 100.0);
int lPct = (int)Math.Round(L * 100.0);
if (includeAlpha)
{
int aPct = (int)Math.Round(A * 100.0);
return string.Format(
CultureInfo.InvariantCulture,
"hsla({0}%, {1}%, {2}%, {3}%)",
hPct,
sPct,
lPct,
aPct);
}
return string.Format(CultureInfo.InvariantCulture, "hsl({0}%, {1}%, {2}%)", hPct, sPct, lPct);
}
#endif
/// <inheritdoc/>
public override string ToString()

64
src/Avalonia.Base/Media/HsvColor.cs

@ -17,10 +17,7 @@ namespace Avalonia.Media
#if !BUILDTASK
public
#endif
readonly struct HsvColor : IEquatable<HsvColor>
#if !BUILDTASK
, IFormattable
#endif
readonly struct HsvColor : IEquatable<HsvColor>, IFormattable
{
/// <summary>
/// Initializes a new instance of the <see cref="HsvColor"/> struct.
@ -210,7 +207,6 @@ namespace Avalonia.Media
return HsvColor.ToHsl(H, S, V, A);
}
#if !BUILDTASK
/// <summary>
/// Returns a formatted string representation of the HSV color.
/// </summary>
@ -247,48 +243,62 @@ namespace Avalonia.Media
"L" or "l" or "L%" or "l%" => ToHsl().ToString(format, formatProvider),
"V" => FormatHsva(),
"v" => FormatHsv(),
"V%" => FormatHsvaPercent(),
"v%" => FormatHsvPercent(),
"V" => FormatHsvCss(),
"v" => FormatHsvCss(includeAlpha: false),
"V%" => FormatHsvPercentCss(),
"v%" => FormatHsvPercentCss(includeAlpha: false),
_ => throw new FormatException($"Format string '{format}' is not supported.")
};
}
private string FormatHsva()
/// <summary>
/// Formats the color as a CSS hsv() or hsva() string using degrees, percent saturation/value,
/// and fractional alpha.
/// </summary>
private string FormatHsvCss(bool includeAlpha = true)
{
int hDeg = (int)Math.Round(H);
int sPct = (int)Math.Round(S * 100.0);
int vPct = (int)Math.Round(V * 100.0);
return string.Format(CultureInfo.InvariantCulture, "hsva({0}, {1}%, {2}%, {3:F2})", hDeg, sPct, vPct, A);
}
private string FormatHsv()
{
int hDeg = (int)Math.Round(H);
int sPct = (int)Math.Round(S * 100.0);
int vPct = (int)Math.Round(V * 100.0);
if (includeAlpha)
{
return string.Format(
CultureInfo.InvariantCulture,
"hsva({0}, {1}%, {2}%, {3:F2})",
hDeg,
sPct,
vPct,
A);
}
return string.Format(CultureInfo.InvariantCulture, "hsv({0}, {1}%, {2}%)", hDeg, sPct, vPct);
}
private string FormatHsvaPercent()
/// <summary>
/// Formats the color as a CSS hsv() or hsva() string with all components as percentages.
/// </summary>
private string FormatHsvPercentCss(bool includeAlpha = true)
{
int hPct = (int)Math.Round(H / 360.0 * 100.0);
int sPct = (int)Math.Round(S * 100.0);
int vPct = (int)Math.Round(V * 100.0);
int aPct = (int)Math.Round(A * 100.0);
return string.Format(CultureInfo.InvariantCulture, "hsva({0}%, {1}%, {2}%, {3}%)", hPct, sPct, vPct, aPct);
}
private string FormatHsvPercent()
{
int hPct = (int)Math.Round(H / 360.0 * 100.0);
int sPct = (int)Math.Round(S * 100.0);
int vPct = (int)Math.Round(V * 100.0);
if (includeAlpha)
{
int aPct = (int)Math.Round(A * 100.0);
return string.Format(
CultureInfo.InvariantCulture,
"hsva({0}%, {1}%, {2}%, {3}%)",
hPct,
sPct,
vPct,
aPct);
}
return string.Format(CultureInfo.InvariantCulture, "hsv({0}%, {1}%, {2}%)", hPct, sPct, vPct);
}
#endif
/// <inheritdoc/>
public override string ToString()

Loading…
Cancel
Save