Editor/WeaponImporter/Tool/UI/Widgets/UiButton.cs
using Editor;
using Sandbox;

namespace WeaponImporter.Tool.UI;

/// <summary>
/// The importer's secondary button: dark fill a step lighter than the card, a hairline border,
/// light text and a hover lighten, with the inputs' corner radius. Every non-primary button uses
/// it so they all look the same. Optional toggle state shows as a green tint.
/// </summary>
public sealed class UiButton : Widget
{
	private string _text;
	private string _icon;
	private bool _checked;

	public Action Clicked { get; set; }

	/// <summary>When set, the button shows as pressed (green tint).</summary>
	public bool IsChecked
	{
		get => _checked;
		set
		{
			if ( _checked == value )
				return;
			_checked = value;
			Update();
		}
	}

	public string Text
	{
		get => _text;
		set
		{
			_text = value ?? "";
			Measure();
			Update();
		}
	}

	public string Icon
	{
		get => _icon;
		set
		{
			_icon = value;
			Measure();
			Update();
		}
	}

	public UiButton( Widget parent, string text, string icon = null, Action clicked = null, string tooltip = null, float height = UiStyle.ControlHeight ) : base( parent )
	{
		_text = text ?? "";
		_icon = icon;
		Clicked = clicked;
		ToolTip = tooltip;
		FixedHeight = height;
		Cursor = CursorShape.Finger;
		MouseTracking = true;
		FocusMode = FocusMode.None;
		Measure();
	}

	private const float PadX = 11f;
	private const float IconSize = 16f;
	private const float IconGap = 6f;

	/// <summary>Estimated width until the first paint measures the real text.</summary>
	private void Measure()
	{
		FixedWidth = WidthFor( _text.Length == 0 ? 0 : 6.2f * _text.Length );
	}

	private float WidthFor( float textWidth )
	{
		if ( _text.Length == 0 )
			return string.IsNullOrEmpty( _icon ) ? PadX * 2 : UiStyle.ControlHeight;
		var icon = string.IsNullOrEmpty( _icon ) ? 0 : IconSize + IconGap;
		return MathF.Ceiling( PadX * 2 + icon + textWidth );
	}

	protected override void OnMouseEnter() => Update();
	protected override void OnMouseLeave() => Update();

	protected override void OnMouseReleased( MouseEvent e )
	{
		base.OnMouseReleased( e );
		if ( !Enabled || !e.LeftMouseButton || !LocalRect.IsInside( e.LocalPosition ) )
			return;
		Clicked?.Invoke();
		e.Accepted = true;
	}

	protected override void OnMousePress( MouseEvent e )
	{
		if ( e.LeftMouseButton )
			e.Accepted = true;
		Update();
	}

	protected override void OnPaint()
	{
		Paint.Antialiasing = true;
		var hover = Paint.HasMouseOver && Enabled;
		var fill = _checked ? Theme.Green.WithAlpha( .18f ) : hover ? Color.Lerp( UiStyle.ButtonFill, Color.White, .06f ) : UiStyle.ButtonFill;
		var edge = _checked ? Theme.Green.WithAlpha( .75f ) : hover ? Color.Lerp( UiStyle.ButtonEdge, Color.White, .1f ) : UiStyle.ButtonEdge;
		Paint.SetPen( edge, 1 );
		Paint.SetBrush( fill );
		Paint.DrawRect( LocalRect.Shrink( .5f ), UiStyle.Radius );

		var color = !Enabled ? Theme.TextDisabled : _checked ? Theme.Green : Theme.Text;
		Paint.SetPen( color );
		if ( _text.Length == 0 )
		{
			if ( !string.IsNullOrEmpty( _icon ) )
				Paint.DrawIcon( LocalRect, _icon, 15, TextFlag.Center );
			return;
		}

		// Fit the width to the measured text so the padding is the same on both sides, and
		// centre icon + text as one group.
		Paint.SetDefaultFont( 8 );
		var textWidth = Paint.MeasureText( _text ).x;
		var wanted = WidthFor( textWidth );
		if ( MathF.Abs( wanted - FixedWidth ) > 0.5f )
			FixedWidth = wanted;

		var hasIcon = !string.IsNullOrEmpty( _icon );
		var group = textWidth + (hasIcon ? IconSize + IconGap : 0);
		var x = LocalRect.Left + MathF.Max( PadX, (LocalRect.Width - group) * 0.5f );
		if ( hasIcon )
		{
			Paint.DrawIcon( new Rect( x, LocalRect.Top, IconSize, LocalRect.Height ), _icon, 15, TextFlag.Center );
			x += IconSize + IconGap;
		}
		Paint.DrawText( new Rect( x, LocalRect.Top, LocalRect.Right - x, LocalRect.Height ), _text, TextFlag.LeftCenter );
	}
}

/// <summary>
/// A read-out that opens a menu when clicked, styled like the framed inputs: current value on the
/// left, a small chevron on the right. Used where a full dropdown would be heavier than needed.
/// </summary>
public sealed class ClickField : Widget
{
	private string _text;
	private readonly string _icon;
	private Color _iconColor;
	private bool _muted;

	public Action Clicked { get; set; }

	public ClickField( Widget parent, string text, string icon, Color iconColor, Action clicked, string tooltip ) : base( parent )
	{
		_text = text ?? "";
		_icon = icon;
		_iconColor = iconColor;
		Clicked = clicked;
		ToolTip = tooltip;
		FixedHeight = UiStyle.ControlHeight;
		MinimumWidth = 40;
		Cursor = CursorShape.Finger;
		MouseTracking = true;
	}

	/// <summary>Muted text for "nothing set" values.</summary>
	public bool Muted
	{
		get => _muted;
		set
		{
			_muted = value;
			Update();
		}
	}

	public void Set( string text, Color iconColor, bool muted )
	{
		_text = text ?? "";
		_iconColor = iconColor;
		_muted = muted;
		Update();
	}

	protected override void OnMouseEnter() => Update();
	protected override void OnMouseLeave() => Update();

	protected override void OnMousePress( MouseEvent e )
	{
		if ( !e.LeftMouseButton || !Enabled )
			return;
		e.Accepted = true;
		Clicked?.Invoke();
	}

	protected override void OnPaint()
	{
		Paint.Antialiasing = true;
		var hover = Paint.HasMouseOver && Enabled;
		Paint.SetPen( hover ? Color.Lerp( UiStyle.ButtonEdge, Color.White, .1f ) : UiStyle.InputEdge, 1 );
		Paint.SetBrush( hover ? Color.Lerp( Theme.WindowBackground, Color.White, .04f ) : Theme.WindowBackground );
		Paint.DrawRect( LocalRect.Shrink( .5f ), UiStyle.Radius );
		var left = 8f;
		if ( !string.IsNullOrEmpty( _icon ) )
		{
			Paint.SetPen( _iconColor );
			Paint.DrawIcon( new Rect( 6, 0, 16, Height ), _icon, 14, TextFlag.Center );
			left = 28f;
		}
		Paint.SetPen( Theme.TextLight );
		Paint.DrawIcon( new Rect( Width - 22, 0, 16, Height ), "expand_more", 14, TextFlag.Center );
		Paint.SetDefaultFont( 8 );
		Paint.SetPen( _muted ? Theme.TextLight : Theme.Text );
		var width = Width - left - 26;
		Paint.DrawText( new Rect( left, 0, width, Height ), Paint.GetElidedText( _text, width, ElideMode.Right, TextFlag.LeftCenter ), TextFlag.LeftCenter );
	}
}