Skip to content
4 changes: 2 additions & 2 deletions src/Controls/src/Core/Platform/Android/TabbedPageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -728,13 +728,13 @@ int GetDefaultColor()
// instead of leaving the application in a broken state
if (IsDarkTheme)
{
defaultColor = ColorUtils.SetAlphaComponent(
defaultColor = AndroidX.Core.Graphics.ColorUtils.SetAlphaComponent(
ContextCompat.GetColor(_context.Context, Resource.Color.primary_dark_material_light),
153); // 60% opacity
}
else
{
defaultColor = ColorUtils.SetAlphaComponent(
defaultColor = AndroidX.Core.Graphics.ColorUtils.SetAlphaComponent(
ContextCompat.GetColor(_context.Context, Resource.Color.primary_dark_material_dark),
153); // 60% opacity
}
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/SourceGen/Controls.SourceGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
<Compile Include="..\..\..\Core\src\Services\Crc64HashAlgorithm.cs">
<Link>Crc64HashAlgorithm.cs</Link>
</Compile>
<Compile Include="..\..\..\Graphics\src\Graphics\ColorUtils.cs" Link="ColorUtils.cs" />
<Compile Include="..\..\..\Graphics\src\Graphics\NumericExtensions.cs" Link="NumericExtensions.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
89 changes: 20 additions & 69 deletions src/Controls/src/SourceGen/TypeConverters/ColorConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,39 @@
using System.Xml;
using Microsoft.CodeAnalysis;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Graphics;

using static Microsoft.Maui.Controls.SourceGen.GeneratorHelpers;

namespace Microsoft.Maui.Controls.SourceGen.TypeConverters;

internal class ColorConverter : ISGTypeConverter
{
private static readonly HashSet<string> KnownNamedColors = new(StringComparer.OrdinalIgnoreCase)
{
"AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black",
"BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse",
"Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue",
"DarkCyan", "DarkGoldenrod", "DarkGray", "DarkGreen", "DarkGrey", "DarkKhaki",
"DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon",
"DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise",
"DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "Firebrick",
"FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "Goldenrod",
"Gray", "Green", "GreenYellow", "Grey", "Honeydew", "HotPink", "IndianRed", "Indigo",
"Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue",
"LightCoral", "LightCyan", "LightGoldenrodYellow", "LightGray", "LightGreen", "LightGrey",
"LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey",
"LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon",
"MediumAquamarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen",
"MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue",
"MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab",
"Orange", "OrangeRed", "Orchid", "PaleGoldenrod", "PaleGreen", "PaleTurquoise", "PaleVioletRed",
"PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown",
"RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver",
"SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen", "SteelBlue", "Tan",
"Teal", "Thistle", "Tomato", "Transparent", "Turquoise", "Violet", "Wheat", "White",
"WhiteSmoke", "Yellow", "YellowGreen"
};

// #rgb, #rrggbb, #aarrggbb are all valid
private const string RxColorHexPattern = @"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}([0-9a-fA-F]{2})?)$";
private static readonly Lazy<Regex> RxColorHex = new(() => new Regex(RxColorHexPattern, RegexOptions.Compiled | RegexOptions.Singleline));

// RGB, RGBA, HSL, HSLA, HSV, HSVA function patterns
private const string RxFuncPattern = "^(?<func>rgba|argb|rgb|hsla|hsl|hsva|hsv)\\(((?<v>\\d%?),){2}((?<v>\\d%?)|(?<v>\\d%?),(?<v>\\d%?))\\);?$";
private static readonly Lazy<Regex> RxFuncExpr = new(() => new Regex(RxFuncPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline));

public IEnumerable<string> SupportedTypes => new[] { "Color", "Microsoft.Maui.Graphics.Color" };

public string Convert(string value, BaseNode node, ITypeSymbol toType, SourceGenContext context, LocalVariable? parentVar = null)
{
var xmlLineInfo = (IXmlLineInfo)node;
if (!string.IsNullOrEmpty(value))
if (ColorUtils.TryParse(value, out float red, out float green, out float blue, out float alpha))
{
// Any named colors are ok. Surrounding white spaces are ok. Case insensitive.
var actualColorName = KnownNamedColors.FirstOrDefault(c => string.Equals(c, value.Trim(), StringComparison.OrdinalIgnoreCase));
if (actualColorName is not null)
{
var colorsType = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Colors")!;
return $"{colorsType.ToFQDisplayString()}.{actualColorName}";
}

// Check for HEX Color string
if (RxColorHex.Value.IsMatch(value))
{
var colorType = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Color")!;
return $"{colorType.ToFQDisplayString()}.FromArgb(\"{value}\")";
}

var match = RxFuncExpr.Value.Match(value);

var funcName = match?.Groups?["func"]?.Value;
var funcValues = match?.Groups?["v"]?.Captures;

if (!string.IsNullOrEmpty(funcName) && funcValues is not null)
{
// ie: argb() needs 4 parameters:
if (funcValues.Count == funcName?.Length)
{
var colorType = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Color")!;
return $"{colorType.ToFQDisplayString()}.Parse(\"{value}\")";
}
}
var colorType = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Color")!;
return $"new {colorType.ToFQDisplayString()}({FormatInvariant(red)}f, {FormatInvariant(green)}f, {FormatInvariant(blue)}f, {FormatInvariant(alpha)}f) /* {value} */";
}

// As a last resort, try Color.Parse() for any other valid color formats
var colorType2 = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Color")!;
return $"{colorType2.ToFQDisplayString()}.Parse(\"{value}\")";
if (GetNamedColorField(value) is IFieldSymbol colorsField)
{
return $"{colorsField.ContainingType.ToFQDisplayString()}.{colorsField.Name}";
}

context.ReportConversionFailed(xmlLineInfo, value, toType, Descriptors.ConversionFailed);
context.ReportConversionFailed((IXmlLineInfo)node, value, toType, Descriptors.ConversionFailed);
return "default";

IFieldSymbol? GetNamedColorField(string name)
{
return context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Graphics.Colors")
?.GetMembers()
.OfType<IFieldSymbol>()
.Where(f => f.IsPublic() && f.IsStatic && f.IsReadOnly && f.Type.ToFQDisplayString() == "global::Microsoft.Maui.Graphics.Color")
.FirstOrDefault(f => string.Equals(f.Name, name, StringComparison.OrdinalIgnoreCase));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public partial class __TypeDBD64C1C77CDA760
{
private partial void InitializeComponent()
{
var color = global::Microsoft.Maui.Graphics.Color.FromArgb("#FF4B14");
var color = new global::Microsoft.Maui.Graphics.Color(1f, 0.29411766f, 0.078431375f, 1f) /* #FF4B14 */;
global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(color!, new global::System.Uri(@"Styles.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 6, 4);
var __root = this;
global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(__root!, new global::System.Uri(@"Styles.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 2, 2);
Expand Down
Loading
Loading