menu_bookDocumentation

Custom Editors - s&box Editor Documentation

calendar_today May 5, 2026 schedule ~1 min read person patrickjr verified 50

Custom Editors

Custom editors let you create specialized inspector interfaces for your components and assets. Build custom widgets, property drawers, and full inspector panels.

Creating a Custom ControlWidget

Control widgets edit individual properties:

CSHARP
[CustomEditor(typeof(MyClass))]
public class MyCustomControlWidget : ControlObjectWidget
{
    public override bool SupportsMultiEdit => false;

    public MyCustomControlWidget(SerializedProperty property) : base(property)
    {
        Layout = Layout.Row();
        Layout.Spacing = 2;

        // Get properties from serialized object
        SerializedObject.TryGetProperty(nameof(MyClass.Color), out var color);
        SerializedObject.TryGetProperty(nameof(MyClass.Name), out var name);

        // Add controls
        Layout.Add( Create(color) );
        Layout.Add( Create(name) );
    }

    protected override void OnPaint()
    {
        // Override to prevent default background
    }
}

Creating a Custom InspectorWidget

Inspector widgets edit entire objects or assets:

CSHARP
[CanEdit("asset:char")]
public class CharacterInspector : Widget, IAssetInspector
{
    CharacterResource Character;
    ControlSheet MainSheet;

    public CharacterInspector( Widget parent ) : base( parent )
    {
        Layout = Layout.Column();
        Layout.Margin = 4;
        Layout.Spacing = 4;

        var header = Layout.Add( new Label( "My Inspector!", this ) );
        header.SetStyles( "font-size: 42px; font-weight: 600; font-family: Poppins" );

        MainSheet = new ControlSheet();
        Layout.Add( MainSheet );
  
        var button = Layout.Add( new Button( "Randomize", "casino", this ) );
        button.Clicked += () =>
        {
            foreach ( var prop in Test.GetSerialized() )
            {
                if ( prop.PropertyType != typeof( float ) ) continue;
                prop.SetValue( Random.Shared.Float( 0, 100 ) );
            }
        };
        
        Layout.AddStretchCell();
        RebuildSheet();
    }

    [EditorEvent.Hotload]
    void RebuildSheet()
    {
        if ( Character is null ) return;
        if ( MainSheet is null ) return;
        MainSheet.Clear( true );
        
        var so = Character.GetSerialized();
        so.OnPropertyChanged += x =>
        {
          Character.StateHasChanged();
        };
        MainSheet.AddObject( so, x => x.PropertyType == typeof( float ) );   
    }

    public void SetAssetPreview( AssetPreview preview )
    {
        // Store preview for later interaction
    }
}

Attribute-Based Editors

Target specific types or attributes:

CSHARP
// Edit all strings with [Password] attribute
[CustomEditor(typeof(string), WithAllAttributes = new[] { typeof(PasswordAttribute) })]
public class PasswordWidget : ControlWidget
{
    // Password input implementation
}

Best Practices

Was this helpful?