menu_bookDocumentation
Custom Editors - s&box Editor Documentation
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
- Support multi-editing when possible (SupportsMultiEdit)
- Handle hotload with [EditorEvent.Hotload]
- Use ControlSheet for property editing
- Clear and rebuild on changes
- Call StateHasChanged() to mark assets dirty
Was this helpful?