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