Editor/ControlWidgets/AnyControlWidget.cs
using Editor;
using ExtendedEditor.Attributes;
using ExtendedEditor.TypeLibraryFixes;
using Sandbox;
using System;
using System.Linq;
namespace ExtendedEditor.ControlWidgets;
[CustomEditor(typeof(Any<>))]
public class AnyControlWidget : ControlObjectWidget
{
public override bool SupportsMultiEdit => false;
private Widget? _valueWidget;
private Type? Type
{
get => _valueProperty?.GetValue<object>()?.GetType();
set
{
if(value == Type)
return;
if(value is null || value.IsAbstract)
{
_valueProperty.SetValue<object?>(null);
return;
}
var newValue = TypeLibrary.Create<object>(value);
_valueProperty.SetValue(newValue);
_valueWidget?.Enabled = newValue is not null;
RebuildLayout();
}
}
private Widget _typeWidget = null!;
private SerializedProperty _valueProperty;
private Type? _lastBuildType;
public AnyControlWidget(SerializedProperty property) : base(property.Fix(), true)
{
_valueProperty = SerializedObject.GetProperty(nameof(Any<>.Value));
Layout = Layout.Column();
Layout.Margin = 4;
Layout.Spacing = 4;
RebuildLayout();
}
private void RebuildLayout()
{
var value = _valueProperty.GetValue<object>();
var currentType = value?.GetType();
if(currentType != null && currentType == _lastBuildType)
return;
Layout.Clear(true);
_valueWidget = null;
_valueProperty = SerializedObject.GetProperty(nameof(Any<>.Value));
var limiter = new TypeSelectAttribute()
{
AllowNone = true,
AllowAbstract = false,
RequiredBaseTypes = [SerializedProperty.PropertyType.GetGenericArguments()[0]]
};
var typePropertyAttributes = SerializedProperty.GetAttributes();
if(typePropertyAttributes.FirstOrDefault(x => x is TypeSelectAttribute) is TypeSelectAttribute outsideLimiter)
{
if(outsideLimiter.ValidatorName is not null)
{
outsideLimiter = new TypeSelectAttribute(outsideLimiter);
outsideLimiter.FindAndAppendValidatorMethod(SerializedProperty);
}
typePropertyAttributes = typePropertyAttributes.Where(x => x is not TypeSelectAttribute);
limiter = outsideLimiter.RestrictBy(limiter);
}
typePropertyAttributes = typePropertyAttributes.Append(limiter);
var typeProperty = TypeLibrary.CreatePropertyFixed("Type", () => Type, t => Type = t,
[.. typePropertyAttributes], SerializedObject);
_lastBuildType = currentType;
if(value is not null)
{
var attributes = SerializedProperty.GetAttributes();
var inline = attributes.FirstOrDefault(x => x is InlineEditorAttribute);
if(inline is not null)
attributes = attributes.Where(x => x is not InlineEditorAttribute).Append(new InlineEditorAttribute() { Label = false });
_valueProperty = _valueProperty.CreateProxy(currentType!, attributes);
}
else if(limiter.AllowNone == false)
{
var types = TypeSelectControlWidget.GetPossibleTypes(typeProperty.GetAttributes());
if(types.Any())
{
var newType = types.First(x => !x.IsAbstract);
if(Type != newType)
{
Type = newType;
return;
}
}
}
_typeWidget = new GenericTypeSelectControlWidget(typeProperty);
Layout.Add(_typeWidget);
if(value is null)
_valueWidget = null;
else
_valueWidget = Layout.Add(Create(_valueProperty));
}
[EditorEvent.Hotload]
private void OnHotload()
{
_lastBuildType = null;
RebuildLayout();
}
protected override void OnValueChanged()
{
RebuildLayout();
}
public override void ChildValuesChanged(Widget source)
{
if(_typeWidget == source || _typeWidget.IsAncestorOf(source))
RebuildLayout();
}
protected override void PaintUnder()
{
bool isInline = SerializedProperty.HasAttribute<InlineEditorAttribute>();
if(isInline)
{
Paint.ClearPen();
Paint.SetBrush(Theme.WidgetBackground);
Paint.DrawRect(LocalRect, Theme.ControlRadius);
return;
}
base.PaintUnder();
}
}