Editor/Prism/Ui/InlineEditors/ColorInlineEditor.cs

An inline UI editor for colours in the editor. It draws a color swatch over a checkerboard, shows "HDR" for values >1, and opens the editor color picker when clicked, committing the selected color back to the plug.

Native Interop
using Editor.Prism.Core;
using Editor.Prism.Serialization;
using Editor.Prism.Ui.Adapters;

namespace Editor.Prism.Ui.InlineEditors;

/// <summary>
/// A colour swatch over a transparency checkerboard, opening the editor's colour picker on click.
/// <para>
/// The checkerboard is not decoration: a colour with alpha 0.1 and a colour that is simply very dark
/// are indistinguishable on a flat dark card, and shader authors set both all the time.
/// </para>
/// </summary>
public sealed class ColorInlineEditor : PrismInlineEditor
{
	/// <summary>Bind a colour editor to a port.</summary>
	public ColorInlineEditor( Plug plug, PrismPlugIn port ) : base( plug, port ) { }

	/// <inheritdoc/>
	protected override float PillHeight => 16f;

	/// <summary>The current colour.</summary>
	public Color Current
	{
		get
		{
			if ( Value is Color color ) return color;

			var components = ValueCodec.ToFloats( Value );

			if ( components is null || components.Length == 0 ) return Color.White;

			return new Color(
				components.Length > 0 ? components[0] : 0f,
				components.Length > 1 ? components[1] : 0f,
				components.Length > 2 ? components[2] : 0f,
				components.Length > 3 ? components[3] : 1f );
		}
	}

	/// <inheritdoc/>
	protected override float MeasureWidth() => 46f;

	/// <inheritdoc/>
	protected override void OnPaintPill( Rect pill )
	{
		var hovered = Paint.HasMouseOver;

		PrismPaint.Swatch( pill, Current,
			hovered ? PrismTheme.BorderStrong.Lighten( 0.2f ) : PrismTheme.BorderSubtle );

		// A colour whose channels leave the zero-to-one range is an HDR value, and that is worth saying.
		var color = Current;
		var hdr = color.r > 1.001f || color.g > 1.001f || color.b > 1.001f;

		if ( !hdr ) return;

		PrismPaint.Text( pill.Shrink( 3f, 0f ), "HDR", PrismTheme.TextOnAccent, 9, 700, TextFlag.RightCenter );
	}

	/// <inheritdoc/>
	protected override void OnMousePressed( GraphicsMouseEvent e )
	{
		if ( !ShouldDraw || !e.LeftMouseButton ) return;
		if ( !PillRect.Grow( 3f ).IsInside( e.LocalPosition ) ) return;

		e.Accepted = true;

		PrismLog.Guard( "Open colour picker", () =>
		{
			var view = Plug?.Node?.GraphicsView;
			var pill = PillRect;
			var screen = view is null
				? Application.CursorPosition
				: view.ToScreen( view.FromScene( ToScene( new Vector2( pill.Left, pill.Bottom + 4f ) ) ) );

			ColorPicker.OpenColorPopup( Current, OnPicked, screen );
		} );
	}

	void OnPicked( Color color ) => Commit( Shape( color ) );
}