Editor/KelvinFloatControlWidget.cs
using System;
using Sandbox;
using RotSoft.Lighting;
using Editor;
namespace RotSoft.Lighting;
/// <summary>
/// A <see cref="FloatControlWidget"/> for float properties marked with <see cref="KelvinSliderAttribute"/>:
/// paints the kelvin (blackbody) gradient as the slider track, with a thermostat icon. The property's
/// <see cref="RangeAttribute"/> supplies the track bounds.
/// </summary>
[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( KelvinSliderAttribute ) } )]
public class KelvinFloatControlWidget : FloatControlWidget
{
/// <summary>Cached kelvin gradient strip, rebuilt only when the bar's width (or DPI) changes.</summary>
Pixmap _gradient;
/// <summary>Pixel size the gradient was baked at; rebuilt when width or DPI changes.</summary>
Vector2 _gradientSize;
public KelvinFloatControlWidget( SerializedProperty property ) : base( property )
{
Icon = "thermostat";
RangeStep = 1f; // snap to whole kelvin so values stay integers, without the [Step] spin arrows
SliderPaint = ( rect, _ ) =>
{
// A thin, full-width track strip (like the regular float slider's track), centered vertically.
const float TrackHeight = 6f;
const float ThumbWidth = 6f;
const float ThumbHeight = 12f;
var centerY = rect.Top + rect.Height / 2f;
var track = new Rect( rect.Left, centerY - TrackHeight / 2f, rect.Width, TrackHeight );
EnsureGradient( track.Width );
if ( _gradient is not null )
Paint.Draw( track, _gradient, 1f, TrackHeight / 2f );
// White thumb at the value position, like the regular float slider.
var current = SerializedProperty.GetValue<float>();
var t = MathX.LerpInverse( current, Range.x, Range.y, true ).Clamp( 0f, 1f );
var thumbX = rect.Left + t * (rect.Width - ThumbWidth);
Paint.Antialiasing = true;
// Even soft shadow ring around the thumb, so no bare thumb edge meets the bright gradient.
Paint.ClearPen();
Paint.Antialiasing = true;
Paint.SetBrush( Color.Black.WithAlpha( 0.35f ) );
Paint.DrawRect( new Rect( thumbX - 1f, centerY - ThumbHeight / 2f - 1f, ThumbWidth + 2f, ThumbHeight + 2f ), 2f );
// Thumb itself.
Paint.ClearPen();
Paint.Antialiasing = true;
Paint.SetBrush( Color.White.Darken( 0.1f ) );
Paint.DrawRect( new Rect( thumbX, centerY - ThumbHeight / 2f, ThumbWidth, ThumbHeight ), 2f );
};
}
/// <summary>Bakes the kelvin gradient into a cached pixmap, rebuilt only when the bar's width (or DPI) changes.</summary>
void EnsureGradient( float width )
{
var dpi = DpiScale;
var size = new Vector2( Math.Max( (int)(width * dpi), 1 ), Math.Max( (int)(10f * dpi), 1 ) );
if ( _gradient is not null && _gradientSize == size ) return;
_gradient = new Pixmap( size );
_gradientSize = size;
using ( Paint.ToPixmap( _gradient ) )
{
Paint.Antialiasing = true;
Paint.ClearPen();
int steps = (int)size.x;
float denom = Math.Max( steps - 1, 1 );
for ( int i = 0; i < steps; i++ )
{
float frac = i / denom;
Paint.SetBrush( new Temperature( MathX.LerpTo( Range.x, Range.y, frac, true ) ).ToColor() );
Paint.DrawRect( new Rect( i, 0, 1f, size.y ) );
}
}
}
}