using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; namespace Terminal.Gui.Drawing; /// /// Helper class for transforming to and from enum. /// internal static class StandardColors { // Lazy initialization to avoid static constructor convoy effect in parallel scenarios private static readonly Lazy> _names = new ( NamesValueFactory, LazyThreadSafetyMode.PublicationOnly); private static ImmutableArray NamesValueFactory () { string [] standardNames = Enum.GetNames ().Order ().ToArray (); return [.. standardNames]; } private static readonly Lazy> _argbNameMap = new ( MapValueFactory, LazyThreadSafetyMode.PublicationOnly); private static FrozenDictionary MapValueFactory () { string [] standardNames = Enum.GetNames () .Order () .ToArray (); Dictionary map = new (standardNames.Length); foreach (string name in standardNames) { var standardColor = Enum.Parse (name); uint argb = GetArgb (standardColor); // TODO: Collect aliases? _ = map.TryAdd (argb, name); } return map.ToFrozenDictionary (); } /// /// Gets read-only list of the W3C colors in alphabetical order. /// public static IReadOnlyList GetColorNames () => _names.Value; /// /// Converts the given Standard (W3C+) color name to equivalent color value. /// /// Standard (W3C+) color name. /// The successfully converted Standard (W3C+) color value. /// True if the conversion succeeded; otherwise false. public static bool TryParseColor (ReadOnlySpan name, out Color color) { if (!Enum.TryParse (name, true, out StandardColor standardColor) || // Any numerical value converts to undefined enum value. !Enum.IsDefined (standardColor)) { color = default (Color); return false; } uint argb = GetArgb (standardColor); color = new (argb); return true; } /// /// Converts the given color value to a Standard (W3C+) color name. /// /// Color value to match Standard (W3C+)color. /// The successfully converted Standard (W3C+) color name. /// True if conversion succeeded; otherwise false. public static bool TryNameColor (Color color, [NotNullWhen (true)] out string? name) { // Ignore alpha channel when matching - alpha represents transparency, not color identity uint opaqueArgb = color.Argb | 0xFF000000; if (_argbNameMap.Value.TryGetValue (opaqueArgb, out name)) { return true; } name = null; return false; } internal static uint GetArgb (StandardColor standardColor) { const int ALPHA_SHIFT = 24; const uint ALPHA_MASK = 0xFFU << ALPHA_SHIFT; var rgb = (int)standardColor; uint argb = (uint)rgb | ALPHA_MASK; return argb; } }