Editor/FloatRangeControlWidget.cs
using Editor;
using ExtendedBox.General;
using Sandbox;
using System;
namespace ExtendedBox;
[CustomEditor(typeof(FloatRange))]
public sealed class FloatRangeControlWidget : ControlWidget
{
private readonly SerializedObject _serializedObject;
private readonly ControlWidget _minControl = null!;
private readonly ControlWidget _maxControl = null!;
private readonly LimitedFloatProperty _minProperty = null!;
private readonly LimitedFloatProperty _maxProperty = null!;
private readonly Layout _fieldsLayout = null!;
private readonly FloatRange? _range;
private readonly bool _rangeClamp;
private readonly float _rangeStep;
private readonly bool _isSlider;
private readonly DoubleFloatSlider? _slider;
private float Min
{
get => SerializedProperty.GetValue<FloatRange>().Min;
set => SerializedProperty.SetValue(SerializedProperty.GetValue<FloatRange>() with { Min = value });
}
private float Max
{
get => SerializedProperty.GetValue<FloatRange>().Max;
set => SerializedProperty.SetValue(SerializedProperty.GetValue<FloatRange>() with { Max = value });
}
public FloatRangeControlWidget(SerializedProperty property) : base(property)
{
property.TryGetAsObject(out _serializedObject);
if(!_serializedObject.IsValid())
{
Log.Warning($"Error when trying to get {property} as object");
return;
}
Layout = Layout.Column();
_fieldsLayout = Layout.Add(Layout.Row());
_fieldsLayout.Spacing = 2f;
if(property.TryGetAttribute<RangeAttribute>(out var rangeAttribute))
{
_range = new(rangeAttribute.Min, rangeAttribute.Max);
_rangeClamp = rangeAttribute.Clamped;
#pragma warning disable CS0618 // Type or member is obsolete
_rangeStep = rangeAttribute.Step;
#pragma warning restore CS0618 // Type or member is obsolete
_isSlider = rangeAttribute.Slider;
}
if(property.TryGetAttribute<StepAttribute>(out var stepAttribute))
{
_rangeStep = stepAttribute.Step;
}
(_minControl, _minProperty) = AddField(nameof(FloatRange.Min), "Min");
(_maxControl, _maxProperty) = AddField(nameof(FloatRange.Max), "Max");
if(_rangeClamp)
{
_minProperty.Min = _range!.Value.Min;
_maxProperty.Max = _range!.Value.Max;
}
if(_isSlider)
{
_slider = new(this)
{
Minimum = _range!.Value.Min,
Maximum = _range!.Value.Max,
ValueA = Min,
ValueB = Max,
Step = _rangeStep,
};
_slider.OnValueEdited += OnSliderValueEdited;
Layout.Add(_slider);
}
}
private void OnSliderValueEdited()
{
var min = Math.Min(_slider!.ValueA, _slider.ValueB);
var max = Math.Max(_slider!.ValueA, _slider.ValueB);
Min = min;
Max = max;
}
private void OnChildValueChanged(Widget widget)
{
if(widget == _minControl || widget.IsDescendantOf(_minControl) ||
widget == _maxControl || widget.IsDescendantOf(_maxControl))
{
if(_slider.IsValid())
{
_slider.ValueA = Min;
_slider.ValueB = Max;
}
}
}
public override void OnDestroyed()
{
_minControl.OnChildValuesChanged -= OnChildValueChanged;
_maxControl.OnChildValuesChanged -= OnChildValueChanged;
}
private (ControlWidget, LimitedFloatProperty) AddField(string propertyName, string text)
{
LimitedFloatProperty property = new(_serializedObject.GetProperty(propertyName));
var controlWidget = Create(property);
controlWidget.OnChildValuesChanged += OnChildValueChanged;
controlWidget.MinimumWidth = Theme.RowHeight;
controlWidget.HorizontalSizeMode = (SizeMode)3;
_fieldsLayout.Add(controlWidget);
return (controlWidget, property);
}
public override void StartEditing()
{
_minControl.StartEditing();
}
protected override void OnPaint()
{
}
private class LimitedFloatProperty : SerializedProperty.Proxy
{
protected override SerializedProperty ProxyTarget { get; }
public float? Min { get; set; }
public float? Max { get; set; }
public LimitedFloatProperty(SerializedProperty proxyTarget)
{
ProxyTarget = proxyTarget;
}
public override void SetValue<T>(T value)
{
var realValue = (float)(object)value;
if(Min.HasValue)
realValue = Math.Max(Min.Value, realValue);
if(Max.HasValue)
realValue = Math.Min(Max.Value, realValue);
base.SetValue(realValue);
}
}
}