Editor/MeterControlWidget.cs

Editor UI widgets for the Metrify package. MeterFloatControlWidget renders a float property annotated with [Meters] as a float control labeled "m" and converting stored inches to metres visually. MeterVectorControlWidget renders a Vector3 with three axis float controls (x,y,z) each shown in metres and color-coded.

File Access
using Editor;
using Sandbox;

namespace Metrify;

/// <summary>
/// Draws a [Meters] float in metres, with an "m" suffix label.
/// </summary>
[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]
public sealed class MeterFloatControlWidget : ControlWidget
{
	public override bool SupportsMultiEdit => true;

	private readonly FloatControlWidget _control;

	public MeterFloatControlWidget( SerializedProperty property ) : base( property )
	{
		HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		Layout = Layout.Row();
		Layout.Spacing = 2;

		_control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( property ) )
		{
			Label = "m",
			HighlightColor = Theme.Green,
			ToolTip = "Metres — stored as inches (s&box world units)"
		}, 1 );
	}

	public override void StartEditing() => _control?.StartEditing();

	protected override void OnPaint()
	{
		// child widget paints itself
	}

	protected override void PaintUnder()
	{
		// nothing
	}
}

/// <summary>
/// Draws a [Meters] Vector3 with each axis in metres.
/// </summary>
[CustomEditor( typeof( Vector3 ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]
public sealed class MeterVectorControlWidget : ControlWidget
{
	public override bool SupportsMultiEdit => true;

	private FloatControlWidget _first;

	public MeterVectorControlWidget( SerializedProperty property ) : base( property )
	{
		HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		if ( !property.TryGetAsObject( out var obj ) )
		{
			Log.Warning( $"[Meters] could not read {property.Name} as an object" );
			return;
		}

		Layout = Layout.Row();
		Layout.Spacing = 2;

		_first = AddAxis( obj, "x", Theme.Red, "X" );
		AddAxis( obj, "y", Theme.Green, "Y" );
		AddAxis( obj, "z", Theme.Blue, "Z" );
	}

	private FloatControlWidget AddAxis( SerializedObject obj, string name, Color color, string label )
	{
		var axis = obj.GetProperty( name );
		if ( axis is null ) return null;

		var control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( axis ) )
		{
			Label = label,
			HighlightColor = color,
			ToolTip = "Metres — stored as inches (s&box world units)"
		}, 1 );

		control.MinimumWidth = Theme.RowHeight;
		control.HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		return control;
	}

	public override void StartEditing() => _first?.StartEditing();

	protected override void OnPaint()
	{
		// child widgets paint themselves
	}

	protected override void PaintUnder()
	{
		// nothing
	}
}