Editor/SmartEnumControlWidget.cs
using Editor;
using Sandbox;
using System;
using System.Linq;

namespace SmarterEnum;

[CustomEditor(typeof(SmartEnum))]
public class SmartEnumControlWidget : ControlWidget
{
    private PopupWidget? _menu;

    public override bool IsControlActive => base.IsControlActive || _menu.IsValid();
    public override bool IsControlButton => true;
    public override bool IsControlHovered => base.IsControlHovered || _menu.IsValid();

    public override bool SupportsMultiEdit => false;


    public SmartEnumControlWidget(SerializedProperty property)
        : base(property)
    {
        Cursor = CursorShape.Finger;
        base.Layout = Layout.Row();
        base.Layout.Spacing = 2f;

        var value = SerializedProperty.GetValue<object>();
        if(value is null && !SerializedProperty.IsNullable)
            SerializedProperty.SetValue(SmartEnum.GetValues(SerializedProperty.PropertyType).FirstOrDefault());
    }

    protected override void PaintControl()
    {
        Color color = (IsControlHovered ? Theme.Blue : Theme.TextControl);
        if(IsControlDisabled)
            color = color.WithAlpha(0.5f);

        Rect localRect = LocalRect;
        localRect = localRect.Shrink(8f, 0f);

        var name = SerializedProperty.GetValue<SmartEnum>()?.Name;

        Paint.SetPen(in color, 0f, PenStyle.Solid);
        Paint.DrawText(in localRect, name ?? "Unset", TextFlag.LeftCenter);

        Paint.SetPen(in color, 0f, PenStyle.Solid);
        Paint.DrawIcon(localRect, "Arrow_Drop_Down", 17f, TextFlag.RightCenter);
    }

    public override void StartEditing()
    {
        if(!IsControlDisabled && !_menu.IsValid())
            OpenMenu();
    }

    protected override void OnMouseClick(MouseEvent e)
    {
        if(!IsControlDisabled && e.LeftMouseButton && !_menu.IsValid())
            OpenMenu();
    }

    protected override void OnDoubleClick(MouseEvent e)
    {
    }

    private SmartEnum? Parse(string name)
    {
        SmartEnum.TryFromName(SerializedProperty.PropertyType, name, false, out var result);
        return result;
    }
    private bool IsCurrent(string name)
    {
        var value = SerializedProperty.GetValue<object>();
        return object.Equals(value, Parse(name));
    }

    private void SetValue(string name)
    {
        SerializedProperty.SetValue(Parse(name) ?? SmartEnum.GetValues(SerializedProperty.PropertyType).First());
    }

    private void OpenMenu()
    {
        PropertyStartEdit();

        _menu = new PopupWidget(null)
        {
            Layout = Layout.Column(),
            MinimumWidth = base.ScreenRect.Width
        };

        _menu.OnLostFocus = (Action)Delegate.Combine(_menu.OnLostFocus, new Action(base.PropertyFinishEdit));

        foreach(var name in SmartEnum.GetValues(SerializedProperty.PropertyType).Select(v => v.Name))
        {
            var button = _menu.Layout.Add(new Widget(_menu));
            button.MinimumSize = 22;
            button.MouseLeftPress = () =>
            {
                SetValue(name);
                _menu.Update();
                _menu.Close();
            };
            button.OnPaintOverride = () => PaintOption(button.LocalRect, name);
        }

        _menu.Position = base.ScreenRect.BottomLeft;
        _menu.Visible = true;
        _menu.AdjustSize();
        _menu.ConstrainToScreen();
        _menu.OnPaintOverride = PaintMenuBackground;
    }

    private bool PaintMenuBackground()
    {
        Paint.SetBrushAndPen(Theme.ControlBackground);
        Rect rect = Paint.LocalRect;
        Paint.DrawRect(in rect, 0f);
        return true;
    }

    private bool PaintOption(Rect rect, string e)
    {
        var active = IsCurrent(e);

        if(Paint.HasMouseOver)
        {
            Paint.ClearPen();
            Paint.SetBrush(Theme.Blue.WithAlpha(0.5f));
            Paint.DrawRect(rect.Shrink(3, 0), 0);
            Paint.SetPen(Theme.Text);
        }
        else
        {
            Paint.SetPen(active ? Theme.Text : Theme.TextControl); // TODO
        }

        rect = rect.Shrink(8, 0);

        if(active)
            Paint.DrawIcon(rect, "check", 13, TextFlag.LeftCenter);

        rect = rect.Shrink(24, 0, 0, 0);

        Paint.SetDefaultFont(8, active ? 1000 : 400);
        Paint.DrawText(rect, e, TextFlag.LeftCenter);
        return true;
    }
}