Editor UI widget for string properties marked with [BoneName]. It renders a dropdown of bone names from a resolved SkinnedModelRenderer instead of a free text field.
#nullable enable annotations
// Inspector picker for [BoneName]-tagged string properties (TwoBoneIK.EndBone,
// FootPlacementIK.LeftFootBone, etc.) - renders a dropdown of the resolved
// SkinnedModelRenderer's actual bone names instead of a plain text box. See
// BoneNameAttribute in Code/BetterIk for the marker attribute this hooks off of.
using System.Collections.Generic;
using System.Linq;
using Editor;
using Sandbox;
using BetterIk;
namespace BetterIk.Editor;
[CustomEditor( typeof( string ), WithAllAttributes = new[] { typeof( BoneNameAttribute ) } )]
public class BoneNameControlWidget : DropdownControlWidget<string>
{
public BoneNameControlWidget( SerializedProperty property ) : base( property )
{
}
protected override IEnumerable<object> GetDropdownValues()
{
var renderer = ResolveRenderer();
if ( renderer?.Model is null )
return Enumerable.Empty<object>();
return renderer.Model.Bones.AllBones
.Select( bone => (object)new Entry { Value = bone.Name, Label = bone.Name } );
}
protected override string GetDisplayText()
{
var value = SerializedProperty.GetValue<string>( null );
return string.IsNullOrEmpty( value ) ? "None" : value;
}
private SkinnedModelRenderer ResolveRenderer()
{
var target = SerializedProperty.Parent?.Targets?.FirstOrDefault();
if ( target is not IHasSkinnedRenderer hasRenderer )
return null;
if ( hasRenderer.Renderer is not null )
return hasRenderer.Renderer;
if ( target is not Component component )
return null;
return component.GameObject?.GetComponent<SkinnedModelRenderer>()
?? component.GameObject?.GetComponentInParent<SkinnedModelRenderer>( includeSelf: true );
}
}